Skip to content

Commit 1a23e20

Browse files
Console command
1 parent 8cd86be commit 1a23e20

File tree

4 files changed

+142
-1
lines changed

4 files changed

+142
-1
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,22 @@ $ composer require --dev camelot/smtp-dev-server
2020

2121
Use
2222
---
23+
24+
To start the server, simply run:
25+
26+
```console
27+
$ vendor/bin/smtp-dev-server
28+
```
29+
30+
This will output all incoming request data to STDOUT.
31+
32+
**NOTE:** Currently the server will also log transactions to `./var/log/smtp.log`
33+
34+
Options
35+
-------
36+
37+
```
38+
-i, --ip=IP TCP/IP address [default: "127.0.0.1"]
39+
-p, --port=PORT Port [default: 2525]
40+
-h, --help Show help
41+
```

bin/smtp-dev-server

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
use Camelot\SmtpDevServer\Server;
5+
use Monolog\Handler\StreamHandler;
6+
use Monolog\Level;
7+
use Monolog\Logger;
8+
use Symfony\Component\Console\Descriptor\TextDescriptor;
9+
use Symfony\Component\Console\Input\ArgvInput;
10+
use Symfony\Component\Console\Input\InputDefinition;
11+
use Symfony\Component\Console\Input\InputOption;
12+
use Symfony\Component\Console\Output\ConsoleOutput;
13+
use Symfony\Component\Console\Style\SymfonyStyle;
14+
15+
$run = function (): void {
16+
if (is_file(dirname(__DIR__) . '/vendor/autoload.php')) {
17+
require_once dirname(__DIR__) . '/vendor/autoload.php';
18+
$logDir = dirname(__DIR__) . '/var/log/smtp.log';
19+
} elseif (is_file(dirname(__DIR__, 3) . '/autoload.php')) {
20+
require_once dirname(__DIR__, 3) . '/autoload.php';
21+
$logDir = dirname(__DIR__, 3) . '/var/log/smtp.log';
22+
} else {
23+
throw new LogicException('Composer autoload is missing. Try running "composer install".');
24+
}
25+
26+
$inputDefinition = new InputDefinition();
27+
$inputDefinition->addOption(new InputOption('ip', 'i', InputOption::VALUE_REQUIRED, 'TCP/IP address', '127.0.0.1'));
28+
$inputDefinition->addOption(new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Port', 2525));
29+
$inputDefinition->addOption(new InputOption('help', 'h', InputOption::VALUE_NONE, 'Show help'));
30+
31+
$input = new ArgvInput(null, $inputDefinition);
32+
$output = new ConsoleOutput();
33+
$io = new SymfonyStyle($input, $output);
34+
35+
if ($input->getOption('help')) {
36+
$io->writeln(
37+
<<<'EOF'
38+
39+
The <info>smtp-dev-server</info> command runs a "fake" SMTP server for development testing needs.
40+
41+
EOF
42+
);
43+
$descriptor = new TextDescriptor();
44+
$descriptor->describe($output, $inputDefinition);
45+
$io->writeln(PHP_EOL);
46+
47+
return;
48+
}
49+
50+
$host = $input->getOption('ip');
51+
$port = $input->getOption('port');
52+
53+
$io->title("Starting SMTP server on {$host} {$port}");
54+
55+
$logger = new Logger('smtp');
56+
$logger->pushHandler(new StreamHandler($logDir, Level::Debug));
57+
$server = new Server($host, $port, $logger);
58+
$server->start();
59+
$io->info('Waiting for connections');
60+
61+
$server->listen(function ($clientId, $message) use ($io): void {
62+
$io->writeln(trim($message));
63+
});
64+
};
65+
66+
$run();

src/Command/SmtpServerCommand.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Camelot\SmtpDevServer\Command;
6+
7+
use Camelot\SmtpDevServer\Server;
8+
use Psr\Log\LoggerInterface;
9+
use Symfony\Component\Console\Attribute\AsCommand;
10+
use Symfony\Component\Console\Command\Command;
11+
use Symfony\Component\Console\Input\InputInterface;
12+
use Symfony\Component\Console\Input\InputOption;
13+
use Symfony\Component\Console\Output\OutputInterface;
14+
use Symfony\Component\Console\Style\SymfonyStyle;
15+
16+
#[AsCommand(
17+
name: 'smtp:server:start',
18+
description: 'Start an SMTP server.',
19+
)]
20+
class SmtpServerCommand extends Command
21+
{
22+
private ?LoggerInterface $logger;
23+
24+
public function __construct(?LoggerInterface $logger = null)
25+
{
26+
parent::__construct();
27+
$this->logger = $logger;
28+
}
29+
30+
protected function configure(): void
31+
{
32+
$this
33+
->addOption('ip', 'i', InputOption::VALUE_REQUIRED, 'TCP/IP address', 'localhost')
34+
->addOption('port', 'p', InputOption::VALUE_REQUIRED, 'Port', 2525)
35+
;
36+
}
37+
38+
protected function execute(InputInterface $input, OutputInterface $output): int
39+
{
40+
$ip = $input->getOption('ip');
41+
$port = $input->getOption('port');
42+
43+
$io = new SymfonyStyle($input, $output);
44+
$io->title("Starting SMTP server on {$ip} {$port}");
45+
46+
$server = new Server($ip, $port, $this->logger);
47+
$server->start();
48+
$io->info('Waiting for connections …');
49+
50+
$server->listen(function ($clientId, $message) use ($output): void {
51+
$output->writeln(trim($message));
52+
});
53+
54+
return Command::SUCCESS;
55+
}
56+
}

src/Server.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ final class Server
2727
/** @var SmtpSocket[] */
2828
private array $sockets = [];
2929

30-
public function __construct(string $host, int $port = 2525, LoggerInterface $logger = null)
30+
public function __construct(string $host = 'localhost', int $port = 2525, LoggerInterface $logger = null)
3131
{
3232
if (!str_contains($host, '://')) {
3333
$host = 'tcp://' . $host . ($port ? ':' . $port : '');

0 commit comments

Comments
 (0)