diff --git a/composer.json b/composer.json
index 7ca1aa1df..e1e45ab1f 100644
--- a/composer.json
+++ b/composer.json
@@ -40,6 +40,7 @@
"doctrine/orm": "^2.13 || ^3",
"doctrine/persistence": "^2 || ^3",
"doctrine/sql-formatter": "^1.0",
+ "fig/log-test": "^1",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan-deprecation-rules": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index fbb09a1c7..1f53d5c6b 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -38,8 +38,7 @@
- tests/TestLogger.php
- src/Tools/Console/ConsoleLogger.php
+ tests/LogUtil.php
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 6d86b7af3..a455f7dff 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -17,9 +17,6 @@ parameters:
-
message: '~^Call to function is_bool\(\) with bool will always evaluate to true\.$~'
path: src/InlineParameterFormatter.php
- -
- message: '~^Call to an undefined method Symfony\\Component\\Console\\Output\\OutputInterface\:\:getErrorOutput\(\)\.$~'
- path: src/Tools/Console/ConsoleLogger.php
# https://github.com/phpstan/phpstan/issues/5982
-
diff --git a/src/Tools/Console/Command/DoctrineCommand.php b/src/Tools/Console/Command/DoctrineCommand.php
index 661f30d54..c1003bce7 100644
--- a/src/Tools/Console/Command/DoctrineCommand.php
+++ b/src/Tools/Console/Command/DoctrineCommand.php
@@ -7,14 +7,15 @@
use Doctrine\Migrations\Configuration\Connection\ConfigurationFile;
use Doctrine\Migrations\Configuration\Migration\ConfigurationFileWithFallback;
use Doctrine\Migrations\DependencyFactory;
-use Doctrine\Migrations\Tools\Console\ConsoleLogger;
use Doctrine\Migrations\Tools\Console\Exception\DependenciesNotSatisfied;
use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage;
use Exception;
use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Style\StyleInterface;
@@ -104,7 +105,16 @@ protected function initialize(InputInterface $input, OutputInterface $output): v
return;
}
- $logger = new ConsoleLogger($output);
+ $logger = new ConsoleLogger($output, [
+ LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
+ LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
+ LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
+ LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
+ LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
+ LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL,
+ LogLevel::INFO => OutputInterface::VERBOSITY_VERBOSE,
+ LogLevel::DEBUG => OutputInterface::VERBOSITY_VERY_VERBOSE,
+ ]);
$dependencyFactory->setService(LoggerInterface::class, $logger);
$dependencyFactory->freeze();
}
diff --git a/src/Tools/Console/ConsoleLogger.php b/src/Tools/Console/ConsoleLogger.php
deleted file mode 100644
index 1cba5a322..000000000
--- a/src/Tools/Console/ConsoleLogger.php
+++ /dev/null
@@ -1,133 +0,0 @@
- */
- private array $verbosityLevelMap = [
- LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
- LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
- LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
- LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
- LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
- LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL,
- LogLevel::INFO => OutputInterface::VERBOSITY_VERBOSE,
- LogLevel::DEBUG => OutputInterface::VERBOSITY_VERY_VERBOSE,
- ];
- /** @var array */
- private array $formatLevelMap = [
- LogLevel::EMERGENCY => self::ERROR,
- LogLevel::ALERT => self::ERROR,
- LogLevel::CRITICAL => self::ERROR,
- LogLevel::ERROR => self::ERROR,
- LogLevel::WARNING => self::INFO,
- LogLevel::NOTICE => self::INFO,
- LogLevel::INFO => self::INFO,
- LogLevel::DEBUG => self::INFO,
- ];
-
- /**
- * @param array $verbosityLevelMap
- * @param array $formatLevelMap
- */
- public function __construct(
- private readonly OutputInterface $output,
- array $verbosityLevelMap = [],
- array $formatLevelMap = [],
- ) {
- $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
- $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
- }
-
- /**
- * {@inheritDoc}
- *
- * @param mixed[] $context
- */
- public function log(mixed $level, string|Stringable $message, array $context = []): void
- {
- if (! isset($this->verbosityLevelMap[$level])) {
- throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
- }
-
- $output = $this->output;
-
- // Write to the error output if necessary and available
- if ($this->formatLevelMap[$level] === self::ERROR) {
- if ($this->output instanceof ConsoleOutputInterface) {
- $output = $output->getErrorOutput();
- }
- }
-
- // the if condition check isn't necessary -- it's the same one that $output will do internally anyway.
- // We only do it for efficiency here as the message formatting is relatively expensive.
- if ($output->getVerbosity() < $this->verbosityLevelMap[$level]) {
- return;
- }
-
- $output->writeln(sprintf('<%1$s>[%2$s] %3$s%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
- }
-
- /**
- * Interpolates context values into the message placeholders.
- *
- * @param mixed[] $context
- */
- private function interpolate(string|Stringable $message, array $context): string
- {
- $message = (string) $message;
- if (! str_contains($message, '{')) {
- return $message;
- }
-
- $replacements = [];
- foreach ($context as $key => $val) {
- if ($val === null || is_scalar($val) || $val instanceof Stringable) {
- $replacements["{{$key}}"] = $val;
- } elseif ($val instanceof DateTimeInterface) {
- $replacements["{{$key}}"] = $val->format(DateTime::RFC3339);
- } elseif (is_object($val)) {
- $replacements["{{$key}}"] = '[object ' . $val::class . ']';
- } else {
- $replacements["{{$key}}"] = '[' . gettype($val) . ']';
- }
-
- if (! isset($replacements["{{$key}}"])) {
- continue;
- }
-
- $replacements["{{$key}}"] = '' . $replacements["{{$key}}"] . '';
- }
-
- return strtr($message, $replacements);
- }
-}
diff --git a/tests/AbstractMigrationTest.php b/tests/AbstractMigrationTest.php
index 24d952b58..25e58bf3d 100644
--- a/tests/AbstractMigrationTest.php
+++ b/tests/AbstractMigrationTest.php
@@ -12,9 +12,12 @@
use Doctrine\Migrations\Query\Query;
use Doctrine\Migrations\Tests\Stub\AbstractMigrationStub;
use Doctrine\Migrations\Tests\Stub\AbstractMigrationWithoutDownStub;
+use Psr\Log\Test\TestLogger;
class AbstractMigrationTest extends MigrationTestCase
{
+ use LogUtil;
+
private AbstractMigrationStub $migration;
private TestLogger $logger;
@@ -73,7 +76,7 @@ public function testWarnIfAddDefaultMessage(): void
public function testWarnIfDontOutputMessageIfFalse(): void
{
$this->migration->warnIf(false, 'trallala');
- self::assertSame('', $this->getLogOutput($this->logger));
+ self::assertSame([], $this->logger->records);
}
public function testWriteInvokesOutputWriter(): void
diff --git a/tests/TestLogger.php b/tests/LogUtil.php
similarity index 59%
rename from tests/TestLogger.php
rename to tests/LogUtil.php
index 7b2e5c6ca..86e1a617d 100644
--- a/tests/TestLogger.php
+++ b/tests/LogUtil.php
@@ -4,50 +4,49 @@
namespace Doctrine\Migrations\Tests;
-use DateTime;
use DateTimeInterface;
-use Psr\Log\AbstractLogger;
+use Psr\Log\Test\TestLogger;
use Stringable;
+use function array_map;
use function gettype;
+use function implode;
use function is_object;
use function is_scalar;
use function str_contains;
use function strtr;
-class TestLogger extends AbstractLogger
+trait LogUtil
{
- /** @var string[] */
- public array $logs = [];
+ private function getLogOutput(TestLogger $logger): string
+ {
+ return implode("\n", $this->getInterpolatedLogRecords($logger));
+ }
- /**
- * {@inheritDoc}
- *
- * @param mixed[] $context
- */
- public function log(mixed $level, string|Stringable $message, array $context = []): void
+ /** @return list */
+ private function getInterpolatedLogRecords(TestLogger $logger): array
{
- $this->logs[] = $this->interpolate($message, $context);
+ return array_map($this->interpolate(...), $logger->records);
}
/**
* Interpolates context values into the message placeholders.
*
- * @param mixed[] $context
+ * @param array{level: mixed, message: string|Stringable, context: mixed[]} $record
*/
- private function interpolate(string|Stringable $message, array $context): string
+ private function interpolate(array $record): string
{
- $message = (string) $message;
+ $message = (string) $record['message'];
if (! str_contains($message, '{')) {
return $message;
}
$replacements = [];
- foreach ($context as $key => $val) {
+ foreach ($record['context'] as $key => $val) {
if ($val === null || is_scalar($val) || $val instanceof Stringable) {
$replacements["{{$key}}"] = $val;
} elseif ($val instanceof DateTimeInterface) {
- $replacements["{{$key}}"] = $val->format(DateTime::RFC3339);
+ $replacements["{{$key}}"] = $val->format(DateTimeInterface::RFC3339);
} elseif (is_object($val)) {
$replacements["{{$key}}"] = '[object ' . $val::class . ']';
} else {
diff --git a/tests/Metadata/Storage/DebugLogger.php b/tests/Metadata/Storage/DebugLogger.php
deleted file mode 100644
index 30ad3b286..000000000
--- a/tests/Metadata/Storage/DebugLogger.php
+++ /dev/null
@@ -1,23 +0,0 @@
-count++;
- }
-}
diff --git a/tests/Metadata/Storage/TableMetadataStorageTest.php b/tests/Metadata/Storage/TableMetadataStorageTest.php
index 8e1d772bf..4fcb0a5ae 100644
--- a/tests/Metadata/Storage/TableMetadataStorageTest.php
+++ b/tests/Metadata/Storage/TableMetadataStorageTest.php
@@ -26,6 +26,7 @@
use Doctrine\Migrations\Version\ExecutionResult;
use Doctrine\Migrations\Version\Version;
use PHPUnit\Framework\TestCase;
+use Psr\Log\Test\TestLogger;
use function sprintf;
@@ -42,7 +43,7 @@ class TableMetadataStorageTest extends TestCase
/** @var AbstractSchemaManager */
private AbstractSchemaManager $schemaManager;
- private DebugLogger $debugLogger;
+ private TestLogger $testLogger;
private function getSqliteConnection(Configuration|null $configuration = null): Connection
{
@@ -54,8 +55,8 @@ private function getSqliteConnection(Configuration|null $configuration = null):
public function setUp(): void
{
$this->connectionConfig = new Configuration();
- $this->debugLogger = new DebugLogger();
- $this->connectionConfig->setMiddlewares([new Middleware($this->debugLogger)]);
+ $this->testLogger = new TestLogger();
+ $this->connectionConfig->setMiddlewares([new Middleware($this->testLogger)]);
$this->connection = $this->getSqliteConnection($this->connectionConfig);
$this->schemaManager = $this->connection->createSchemaManager();
@@ -67,13 +68,12 @@ public function testSchemaIntrospectionExecutedOnlyOnce(): void
{
$this->storage->ensureInitialized();
- $oldQueryCount = $this->debugLogger->count;
+ $this->testLogger->reset();
$this->storage->ensureInitialized();
- self::assertSame(0, $this->debugLogger->count - $oldQueryCount);
+ self::assertCount(0, $this->testLogger->records);
- $oldQueryCount = $this->debugLogger->count;
$this->storage->getExecutedMigrations();
- self::assertSame(1, $this->debugLogger->count - $oldQueryCount);
+ self::assertCount(1, $this->testLogger->records);
}
public function testDifferentTableNotUpdatedOnRead(): void
diff --git a/tests/MigrationTestCase.php b/tests/MigrationTestCase.php
index 28b14c48a..32f2e091d 100644
--- a/tests/MigrationTestCase.php
+++ b/tests/MigrationTestCase.php
@@ -8,8 +8,6 @@
use Doctrine\DBAL\DriverManager;
use PHPUnit\Framework\TestCase;
-use function implode;
-
abstract class MigrationTestCase extends TestCase
{
public function getSqliteConnection(): Connection
@@ -18,9 +16,4 @@ public function getSqliteConnection(): Connection
return DriverManager::getConnection($params);
}
-
- public function getLogOutput(TestLogger $logger): string
- {
- return implode("\n", $logger->logs);
- }
}
diff --git a/tests/MigratorTest.php b/tests/MigratorTest.php
index bfe910d8c..58b492f19 100644
--- a/tests/MigratorTest.php
+++ b/tests/MigratorTest.php
@@ -23,6 +23,7 @@
use Doctrine\Migrations\Version\Direction;
use Doctrine\Migrations\Version\Version;
use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\Test\TestLogger;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Stopwatch\Stopwatch;
use Throwable;
@@ -79,8 +80,8 @@ public function testEmptyPlanShowsMessage(): void
$planList = new MigrationPlanList([], Direction::UP);
$migrator->migrate($planList, $this->migratorConfiguration);
- self::assertCount(1, $this->logger->logs, 'should output the no migrations message');
- self::assertStringContainsString('No migrations', $this->logger->logs[0]);
+ self::assertCount(1, $this->logger->records, 'should output the no migrations message');
+ self::assertStringContainsString('No migrations', $this->logger->records[0]['message']);
}
protected function createTestMigrator(): DbalMigrator
diff --git a/tests/Tools/Console/Command/MigrationVersionTest.php b/tests/Tools/Console/Command/MigrationVersionTest.php
index 13b303e26..a57209ddd 100644
--- a/tests/Tools/Console/Command/MigrationVersionTest.php
+++ b/tests/Tools/Console/Command/MigrationVersionTest.php
@@ -13,12 +13,12 @@
use Doctrine\Migrations\MigrationsRepository;
use Doctrine\Migrations\Tests\Helper;
use Doctrine\Migrations\Tests\MigrationTestCase;
-use Doctrine\Migrations\Tests\TestLogger;
use Doctrine\Migrations\Tools\Console\Command\VersionCommand;
use Doctrine\Migrations\Version\Direction;
use Doctrine\Migrations\Version\ExecutionResult;
use Doctrine\Migrations\Version\Version;
use InvalidArgumentException;
+use Psr\Log\NullLogger;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Tester\CommandTester;
@@ -40,13 +40,12 @@ protected function setUp(): void
$configuration = new Configuration();
$configuration->addMigrationsDirectory('DoctrineMigrations', sys_get_temp_dir());
- $conn = $this->getSqliteConnection();
- $logger = new TestLogger();
+ $conn = $this->getSqliteConnection();
$dependencyFactory = DependencyFactory::fromConnection(
new ExistingConfiguration($configuration),
new ExistingConnection($conn),
- $logger,
+ new NullLogger(),
);
$this->migrationRepository = $dependencyFactory->getMigrationRepository();
diff --git a/tests/Tools/Console/ConsoleLoggerTest.php b/tests/Tools/Console/ConsoleLoggerTest.php
deleted file mode 100644
index b8aac2d91..000000000
--- a/tests/Tools/Console/ConsoleLoggerTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-output = new BufferedOutput();
- }
-
- public function testNoInfoAndDebugAsDefault(): void
- {
- $logger = new ConsoleLogger($this->output);
- $logger->info('foo');
- $logger->debug('bar');
-
- self::assertSame('', $this->output->fetch());
- }
-
- public function testLevelCanBeChanged(): void
- {
- $logger = new ConsoleLogger($this->output, [LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL]);
- $logger->info('foo');
- $logger->debug('bar');
-
- self::assertSame('[info] foo' . PHP_EOL, $this->output->fetch());
- }
-
- public function testVerbosityIsREspected(): void
- {
- $this->output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
-
- $logger = new ConsoleLogger($this->output);
- $logger->info('foo');
- $logger->debug('bar');
-
- self::assertSame(
- '[info] foo' . PHP_EOL . '[debug] bar' . PHP_EOL,
- $this->output->fetch(),
- );
- }
-
- public function testInterpolation(): void
- {
- $logger = new ConsoleLogger($this->output);
- $logger->error('foo {number} {date} {object} {resource} {missing} bar', [
- 'number' => 1,
- 'date' => new DateTime('2010-01-01 00:08:09+00:00'),
- 'object' => new stdClass(),
- 'resource' => fopen('php://output', 'w'),
- ]);
- self::assertSame(
- '[error] foo 1 2010-01-01T00:08:09+00:00 [object stdClass] [resource] {missing} bar' . PHP_EOL,
- $this->output->fetch(),
- );
- }
-}
diff --git a/tests/Version/ExecutorTest.php b/tests/Version/ExecutorTest.php
index 31acf865d..cf5d8746d 100644
--- a/tests/Version/ExecutorTest.php
+++ b/tests/Version/ExecutorTest.php
@@ -14,7 +14,7 @@
use Doctrine\Migrations\ParameterFormatter;
use Doctrine\Migrations\Provider\SchemaDiffProvider;
use Doctrine\Migrations\Query\Query;
-use Doctrine\Migrations\Tests\TestLogger;
+use Doctrine\Migrations\Tests\LogUtil;
use Doctrine\Migrations\Tests\Version\Fixture\EmptyTestMigration;
use Doctrine\Migrations\Tests\Version\Fixture\VersionExecutorTestMigration;
use Doctrine\Migrations\Version\DbalExecutor;
@@ -25,6 +25,7 @@
use Exception;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use Psr\Log\Test\TestLogger;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Stopwatch\StopwatchEvent;
use Symfony\Component\Stopwatch\StopwatchPeriod;
@@ -32,6 +33,8 @@
class ExecutorTest extends TestCase
{
+ use LogUtil;
+
/** @var Connection&MockObject */
private Connection $connection;
@@ -90,7 +93,7 @@ public function testExecuteWithNoQueries(): void
'++ migrating xx',
'Migration xx was executed but did not result in any SQL statements.',
'Migration xx migrated (took 100ms, used 100 memory)',
- ], $this->logger->logs);
+ ], $this->getInterpolatedLogRecords($this->logger));
}
public function testExecuteUp(): void
@@ -137,7 +140,7 @@ public function testExecuteUp(): void
3 => 'SELECT 2 ',
4 => 'Query took 100ms',
5 => 'Migration test migrated (took 100ms, used 100 memory)',
- ], $this->logger->logs);
+ ], $this->getInterpolatedLogRecords($this->logger));
}
/** @test */
@@ -151,7 +154,7 @@ public function executeUpShouldAppendDescriptionWhenItIsNotEmpty(): void
$this->versionExecutor->execute($plan, $migratorConfiguration);
- self::assertSame('++ migrating test (testing)', $this->logger->logs[0]);
+ self::assertSame('++ migrating test (testing)', $this->getInterpolatedLogRecords($this->logger)[0]);
}
public function testExecuteDown(): void
@@ -198,7 +201,7 @@ public function testExecuteDown(): void
3 => 'SELECT 4 ',
4 => 'Query took 100ms',
5 => 'Migration test reverted (took 100ms, used 100 memory)',
- ], $this->logger->logs);
+ ], $this->getInterpolatedLogRecords($this->logger));
}
public function testExecuteDryRun(): void
@@ -261,7 +264,7 @@ public function testExecuteDryRun(): void
'SELECT 1 ',
'SELECT 2 ',
'Migration test migrated (took 100ms, used 100 memory)',
- ], $this->logger->logs);
+ ], $this->getInterpolatedLogRecords($this->logger));
}
/** @test */
@@ -498,7 +501,7 @@ public function executeDownShouldAppendDescriptionWhenItIsNotEmpty(): void
$migratorConfiguration,
);
- self::assertSame('++ reverting test', $this->logger->logs[0]);
+ self::assertSame('++ reverting test', $this->getInterpolatedLogRecords($this->logger)[0]);
}
protected function setUp(): void