From 697fca1598ebea7db4457535d78dfc77fdc441f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 4 Jul 2025 10:51:27 +0200 Subject: [PATCH 1/2] [Console] Restore SHELL_VERBOSITY after a command is ran --- src/Symfony/Component/Console/Application.php | 18 ++++ .../Console/Tester/ApplicationTester.php | 38 ++----- .../Console/Tests/ApplicationTest.php | 99 ++++++++++++++++++- 3 files changed, 125 insertions(+), 30 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index f0e0a303ee905..1ea644df04263 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -186,6 +186,8 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu } } + $prevShellVerbosity = getenv('SHELL_VERBOSITY'); + try { $this->configureIO($input, $output); @@ -223,6 +225,22 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu $phpHandler[0]->setExceptionHandler($finalHandler); } } + + // SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it + // to its previous value to avoid one command verbosity to spread to other commands + if (false === $prevShellVerbosity) { + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY'); + } + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + } else { + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY='.$prevShellVerbosity); + } + $_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity; + $_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity; + } } if ($this->autoExit) { diff --git a/src/Symfony/Component/Console/Tester/ApplicationTester.php b/src/Symfony/Component/Console/Tester/ApplicationTester.php index cebb6f8ebf34a..a6dc8e1ce5178 100644 --- a/src/Symfony/Component/Console/Tester/ApplicationTester.php +++ b/src/Symfony/Component/Console/Tester/ApplicationTester.php @@ -47,37 +47,17 @@ public function __construct( */ public function run(array $input, array $options = []): int { - $prevShellVerbosity = getenv('SHELL_VERBOSITY'); - - try { - $this->input = new ArrayInput($input); - if (isset($options['interactive'])) { - $this->input->setInteractive($options['interactive']); - } + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } - if ($this->inputs) { - $this->input->setStream(self::createStream($this->inputs)); - } + if ($this->inputs) { + $this->input->setStream(self::createStream($this->inputs)); + } - $this->initOutput($options); + $this->initOutput($options); - return $this->statusCode = $this->application->run($this->input, $this->output); - } finally { - // SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it - // to its previous value to avoid one test's verbosity to spread to the following tests - if (false === $prevShellVerbosity) { - if (\function_exists('putenv')) { - @putenv('SHELL_VERBOSITY'); - } - unset($_ENV['SHELL_VERBOSITY']); - unset($_SERVER['SHELL_VERBOSITY']); - } else { - if (\function_exists('putenv')) { - @putenv('SHELL_VERBOSITY='.$prevShellVerbosity); - } - $_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity; - $_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity; - } - } + return $this->statusCode = $this->application->run($this->input, $this->output); } } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 268f8ba501a9e..6390d4828fb79 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -37,6 +37,7 @@ use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\Output; @@ -831,7 +832,7 @@ public function testSetCatchErrors(bool $catchExceptions) try { $tester->run(['command' => 'boom']); - $this->fail('The exception is not catched.'); + $this->fail('The exception is not caught.'); } catch (\Throwable $e) { $this->assertInstanceOf(\Error::class, $e); $this->assertSame('This is an error.', $e->getMessage()); @@ -2463,6 +2464,102 @@ private function createSignalableApplication(Command $command, ?EventDispatcherI return $application; } + + public function testShellVerbosityIsRestoredAfterCommandExecutionWithInitialValue() + { + // Set initial SHELL_VERBOSITY + putenv('SHELL_VERBOSITY=-2'); + $_ENV['SHELL_VERBOSITY'] = '-2'; + $_SERVER['SHELL_VERBOSITY'] = '-2'; + + $application = new Application(); + $application->setAutoExit(false); + $application->register('foo') + ->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('SHELL_VERBOSITY: '.$_SERVER['SHELL_VERBOSITY']); + + return 0; + }); + + $input = new ArrayInput(['command' => 'foo', '--verbose' => 3]); + $output = new BufferedOutput(); + + $application->run($input, $output); + + $this->assertSame('SHELL_VERBOSITY: 3', $output->fetch()); + $this->assertSame('-2', getenv('SHELL_VERBOSITY')); + $this->assertSame('-2', $_ENV['SHELL_VERBOSITY']); + $this->assertSame('-2', $_SERVER['SHELL_VERBOSITY']); + + // Clean up for other tests + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + } + + public function testShellVerbosityIsRemovedAfterCommandExecutionWhenNotSetInitially() + { + // Ensure SHELL_VERBOSITY is not set initially + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + + $application = new Application(); + $application->setAutoExit(false); + $application->register('foo') + ->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('SHELL_VERBOSITY: '.$_SERVER['SHELL_VERBOSITY']); + + return 0; + }); + + $input = new ArrayInput(['command' => 'foo', '--verbose' => 3]); + $output = new BufferedOutput(); + + $application->run($input, $output); + + $this->assertSame('SHELL_VERBOSITY: 3', $output->fetch()); + $this->assertFalse(getenv('SHELL_VERBOSITY')); + $this->assertArrayNotHasKey('SHELL_VERBOSITY', $_ENV); + $this->assertArrayNotHasKey('SHELL_VERBOSITY', $_SERVER); + } + + public function testShellVerbosityDoesNotLeakBetweenCommandExecutions() + { + // Ensure no initial SHELL_VERBOSITY + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + + $application = new Application(); + $application->setAutoExit(false); + $application->register('verbose-cmd') + ->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('SHELL_VERBOSITY: '.$_SERVER['SHELL_VERBOSITY']); + + return 0; + }); + $application->register('normal-cmd') + ->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('SHELL_VERBOSITY: '.$_SERVER['SHELL_VERBOSITY']); + + return 0; + }); + + $output = new BufferedOutput(); + + $application->run(new ArrayInput(['command' => 'verbose-cmd', '--verbose' => true]), $output); + + $this->assertSame('SHELL_VERBOSITY: 1', $output->fetch(), 'SHELL_VERBOSITY should be set to 1 for verbose command'); + $this->assertFalse(getenv('SHELL_VERBOSITY'), 'SHELL_VERBOSITY should not be set after first command'); + + $application->run(new ArrayInput(['command' => 'normal-cmd']), $output); + + $this->assertSame('SHELL_VERBOSITY: 0', $output->fetch(), 'SHELL_VERBOSITY should not leak to second command'); + $this->assertFalse(getenv('SHELL_VERBOSITY'), 'SHELL_VERBOSITY should not leak to second command'); + $this->assertArrayNotHasKey('SHELL_VERBOSITY', $_ENV); + $this->assertArrayNotHasKey('SHELL_VERBOSITY', $_SERVER); + } } class CustomApplication extends Application From 45c3203b96a7f76c41f6b13d6075e1bcd07999aa Mon Sep 17 00:00:00 2001 From: Antonio Torres <201475551+atgitwk@users.noreply.github.com> Date: Sun, 13 Apr 2025 21:24:45 +0200 Subject: [PATCH 2/2] [Messenger] Fix Oracle errors 'ORA-00955: Name is already used by an existing object' with Doctrine transport --- .../Doctrine/Tests/Transport/ConnectionTest.php | 6 ++++++ .../Bridge/Doctrine/Transport/Connection.php | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php index e8716ff196ad9..c8b07fc0f5e0f 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php @@ -868,6 +868,12 @@ public function testConfigureSchemaOracleSequenceNameSuffixed() { $driverConnection = $this->createMock(DBALConnection::class); $driverConnection->method('getDatabasePlatform')->willReturn(new OraclePlatform()); + + // Mock the result returned by executeQuery to be an Oracle version 12.1.0 or higher. + $result = $this->createMock(Result::class); + $result->method('fetchOne')->willReturn('12.1.0'); + $driverConnection->method('executeQuery')->willReturn($result); + $schema = new Schema(); $connection = new Connection(['table_name' => 'messenger_messages'], $driverConnection); diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php index 99e9b0bccb022..daa93b2b0d1e8 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php @@ -338,7 +338,9 @@ public function setup(): void throw new \TypeError(\sprintf('The table name must be an instance of "%s" or a string ("%s" given).', AbstractAsset::class, get_debug_type($tableName))); } - return $tableName === $this->configuration['table_name']; + // SchemaAssetsFilter needs to match the messenger table name and also the messenger sequence name to make $schemaDiff work correctly in updateSchema() + // This may also work for other databases if their sequence name is suffixed with '_seq', '_id_seq' or similar. + return str_starts_with($tableName, $this->configuration['table_name']); // MESSENGER_MESSAGES* }); $this->updateSchema(); $configuration->setSchemaAssetsFilter($assetFilter); @@ -569,9 +571,13 @@ private function addTableToSchema(Schema $schema): void // We need to create a sequence for Oracle and set the id column to get the correct nextval if ($this->driverConnection->getDatabasePlatform() instanceof OraclePlatform) { - $idColumn->setDefault($this->configuration['table_name'].self::ORACLE_SEQUENCES_SUFFIX.'.nextval'); + $serverVersion = $this->driverConnection->executeQuery("SELECT version FROM product_component_version WHERE product LIKE 'Oracle Database%'")->fetchOne(); + if (version_compare($serverVersion, '12.1.0', '>=')) { + $idColumn->setAutoincrement(false); // disable the creation of SEQUENCE and TRIGGER + $idColumn->setDefault($this->configuration['table_name'].self::ORACLE_SEQUENCES_SUFFIX.'.nextval'); - $schema->createSequence($this->configuration['table_name'].self::ORACLE_SEQUENCES_SUFFIX); + $schema->createSequence($this->configuration['table_name'].self::ORACLE_SEQUENCES_SUFFIX); + } } }