Skip to content

Commit

Permalink
Merge branch '7.0' into 7.1
Browse files Browse the repository at this point in the history
* 7.0:
  [FrameworkBundle] Add TemplateController to the list of allowed controllers for fragments
  [Cache][Lock] Fix PDO store not creating table + add tests
  Closes #51936-Added Missing translations for Czech (cs) in validators.cs.xlf file
  Added missing translations in turkish and updated validators.tr.xlf
  [Serializer] Fix denormalizing date intervals having both weeks and days
  [Validator] updated Turkish translation
  [Serializer] Remove wrong final tags
  [Serializer] Fix denormalize constructor arguments
  Add some more non-countable English nouns
  Add hint that changing input arguments has no effect
  register the virtual request stack together with common profiling services
  Don't lose checkpoint state when lock is acquired from another
  [DomCrawler] Revert "bug #52579 UriResolver support path with colons"
  [DoctrineBridge] Fix use "attribute" driver by default
  [VarExporter] Fix handling mangled property names returned by __sleep()
  Update Github template for 7.1
  [WebProfilerBundle] Mark CodeExtension as non-internal
  Fix memory limit in PhpSubprocess unit test
  • Loading branch information
nicolas-grekas committed Nov 20, 2023
2 parents 5689df4 + 28cc56d commit 79f841c
Show file tree
Hide file tree
Showing 43 changed files with 430 additions and 113 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
| Branch? | 6.4 for features / 5.4 or 6.3 for bug fixes <!-- see below -->
| Branch? | 7.1 for features / 5.4, 6.3, 6.4, or 7.0 for bug fixes <!-- see below -->
| Bug fix? | yes/no
| New feature? | yes/no <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | yes/no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
Expand Down
Expand Up @@ -90,8 +90,8 @@ protected function loadMappingInformation(array $objectManager, ContainerBuilder
if (!$mappingConfig) {
continue;
}
} else {
$mappingConfig['type'] ??= 'attribute';
} elseif (!$mappingConfig['type']) {
$mappingConfig['type'] = 'attribute';
}

$this->assertValidMappingConfiguration($mappingConfig, $objectManager['name']);
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Console/Application.php
Expand Up @@ -107,6 +107,12 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
}

(new SymfonyStyle($input, $output))->warning('The "--profile" option needs the Stopwatch component. Try running "composer require symfony/stopwatch".');
} elseif (!$container->has('.virtual_request_stack')) {
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}

(new SymfonyStyle($input, $output))->warning('The "--profile" option needs the profiler integration. Try enabling the "framework.profiler" option.');
} else {
$command = new TraceableCommand($command, $container->get('debug.stopwatch'));

Expand Down
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class VirtualRequestStackPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if ($container->has('.virtual_request_stack')) {
return;
}

if ($container->hasDefinition('debug.event_dispatcher')) {
$container->getDefinition('debug.event_dispatcher')->replaceArgument(3, new Reference('request_stack', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
}

if ($container->hasDefinition('debug.log_processor')) {
$container->getDefinition('debug.log_processor')->replaceArgument(0, new Reference('request_stack'));
}
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -20,6 +20,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\VirtualRequestStackPass;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
Expand Down Expand Up @@ -171,6 +172,7 @@ public function build(ContainerBuilder $container): void
$container->addCompilerPass(new RemoveUnusedSessionMarshallingHandlerPass());
// must be registered after MonologBundle's LoggerChannelPass
$container->addCompilerPass(new ErrorLoggerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
$container->addCompilerPass(new VirtualRequestStackPass());

if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2);
Expand Down
5 changes: 0 additions & 5 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.php
Expand Up @@ -15,7 +15,6 @@
use Symfony\Component\HttpKernel\Controller\TraceableArgumentResolver;
use Symfony\Component\HttpKernel\Controller\TraceableControllerResolver;
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
use Symfony\Component\HttpKernel\Debug\VirtualRequestStack;

return static function (ContainerConfigurator $container) {
$container->services()
Expand Down Expand Up @@ -47,9 +46,5 @@
->set('argument_resolver.not_tagged_controller', NotTaggedControllerValueResolver::class)
->args([abstract_arg('Controller argument, set in FrameworkExtension')])
->tag('controller.argument_value_resolver', ['priority' => -200])

->set('.virtual_request_stack', VirtualRequestStack::class)
->args([service('request_stack')])
->public()
;
};
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Bundle\FrameworkBundle\EventListener\ConsoleProfilerListener;
use Symfony\Component\HttpKernel\Debug\VirtualRequestStack;
use Symfony\Component\HttpKernel\EventListener\ProfilerListener;
use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage;
use Symfony\Component\HttpKernel\Profiler\Profiler;
Expand Down Expand Up @@ -45,5 +46,9 @@
service('router'),
])
->tag('kernel.event_subscriber')

->set('.virtual_request_stack', VirtualRequestStack::class)
->args([service('request_stack')])
->public()
;
};
3 changes: 2 additions & 1 deletion src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php
Expand Up @@ -13,6 +13,7 @@

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
use Symfony\Bundle\FrameworkBundle\Controller\TemplateController;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DateTimeValueResolver;
Expand Down Expand Up @@ -41,7 +42,7 @@
service('service_container'),
service('logger')->ignoreOnInvalid(),
])
->call('allowControllers', [[AbstractController::class]])
->call('allowControllers', [[AbstractController::class, TemplateController::class]])
->tag('monolog.logger', ['channel' => 'request'])

->set('argument_metadata_factory', ArgumentMetadataFactory::class)
Expand Down
Expand Up @@ -22,8 +22,6 @@
* that is never executed in a production environment.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
final class CodeExtension extends AbstractExtension
{
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php
Expand Up @@ -375,7 +375,8 @@ private function getServerVersion(): string
return $this->serverVersion;
}

$conn = $this->conn->getWrappedConnection();
// The condition should be removed once support for DBAL <3.3 is dropped
$conn = method_exists($this->conn, 'getNativeConnection') ? $this->conn->getNativeConnection() : $this->conn->getWrappedConnection();
if ($conn instanceof ServerInfoAwareConnection) {
return $this->serverVersion = $conn->getServerVersion();
}
Expand Down
21 changes: 19 additions & 2 deletions src/Symfony/Component/Cache/Adapter/PdoAdapter.php
Expand Up @@ -284,7 +284,7 @@ protected function doSave(array $values, int $lifetime): array|bool
try {
$stmt = $conn->prepare($sql);
} catch (\PDOException) {
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
$this->createTable();
}
$stmt = $conn->prepare($sql);
Expand Down Expand Up @@ -319,7 +319,7 @@ protected function doSave(array $values, int $lifetime): array|bool
try {
$stmt->execute();
} catch (\PDOException) {
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
$this->createTable();
}
$stmt->execute();
Expand Down Expand Up @@ -367,4 +367,21 @@ private function getServerVersion(): string
{
return $this->serverVersion ??= $this->conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
}

private function isTableMissing(\PDOException $exception): bool
{
$driver = $this->driver;
$code = $exception->getCode();

switch (true) {
case 'pgsql' === $driver && '42P01' === $code:
case 'sqlite' === $driver && str_contains($exception->getMessage(), 'no such table:'):
case 'oci' === $driver && 942 === $code:
case 'sqlsrv' === $driver && 208 === $code:
case 'mysql' === $driver && 1146 === $code:
return true;
default:
return false;
}
}
}
Expand Up @@ -24,6 +24,8 @@
use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter;

/**
* @requires extension pdo_sqlite
*
* @group time-sensitive
*/
class DoctrineDbalAdapterTest extends AdapterTestCase
Expand All @@ -32,10 +34,6 @@ class DoctrineDbalAdapterTest extends AdapterTestCase

public static function setUpBeforeClass(): void
{
if (!\extension_loaded('pdo_sqlite')) {
self::markTestSkipped('Extension pdo_sqlite required.');
}

self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
}

Expand Down Expand Up @@ -107,13 +105,12 @@ public function testConfigureSchemaTableExists()
}

/**
* @dataProvider provideDsn
* @dataProvider provideDsnWithSQLite
*/
public function testDsn(string $dsn, string $file = null)
public function testDsnWithSQLite(string $dsn, string $file = null)
{
try {
$pool = new DoctrineDbalAdapter($dsn);
$pool->createTable();

$item = $pool->getItem('key');
$item->set('value');
Expand All @@ -125,12 +122,35 @@ public function testDsn(string $dsn, string $file = null)
}
}

public static function provideDsn(): \Generator
public static function provideDsnWithSQLite()
{
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
yield ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
yield ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
yield ['sqlite://localhost/:memory:'];
yield 'SQLite file' => ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
yield 'SQLite3 file' => ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
yield 'SQLite in memory' => ['sqlite://localhost/:memory:'];
}

/**
* @requires extension pdo_pgsql
*
* @group integration
*/
public function testDsnWithPostgreSQL()
{
if (!$host = getenv('POSTGRES_HOST')) {
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
}

try {
$pool = new DoctrineDbalAdapter('pgsql://postgres:password@'.$host);

$item = $pool->getItem('key');
$item->set('value');
$this->assertTrue($pool->save($item));
} finally {
$pdo = new \PDO('pgsql:host='.$host.';user=postgres;password=password');
$pdo->exec('DROP TABLE IF EXISTS cache_items');
}
}

protected function isPruned(DoctrineDbalAdapter $cache, string $name): bool
Expand Down
42 changes: 32 additions & 10 deletions src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php
Expand Up @@ -15,6 +15,8 @@
use Symfony\Component\Cache\Adapter\PdoAdapter;

/**
* @requires extension pdo_sqlite
*
* @group time-sensitive
*/
class PdoAdapterTest extends AdapterTestCase
Expand All @@ -23,10 +25,6 @@ class PdoAdapterTest extends AdapterTestCase

public static function setUpBeforeClass(): void
{
if (!\extension_loaded('pdo_sqlite')) {
self::markTestSkipped('Extension pdo_sqlite required.');
}

self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');

$pool = new PdoAdapter('sqlite:'.self::$dbFile);
Expand Down Expand Up @@ -68,13 +66,12 @@ public function testCleanupExpiredItems()
}

/**
* @dataProvider provideDsn
* @dataProvider provideDsnSQLite
*/
public function testDsn(string $dsn, string $file = null)
public function testDsnWithSQLite(string $dsn, string $file = null)
{
try {
$pool = new PdoAdapter($dsn);
$pool->createTable();

$item = $pool->getItem('key');
$item->set('value');
Expand All @@ -86,11 +83,36 @@ public function testDsn(string $dsn, string $file = null)
}
}

public static function provideDsn()
public static function provideDsnSQLite()
{
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
yield ['sqlite:'.$dbFile.'2', $dbFile.'2'];
yield ['sqlite::memory:'];
yield 'SQLite file' => ['sqlite:'.$dbFile.'2', $dbFile.'2'];
yield 'SQLite in memory' => ['sqlite::memory:'];
}

/**
* @requires extension pdo_pgsql
*
* @group integration
*/
public function testDsnWithPostgreSQL()
{
if (!$host = getenv('POSTGRES_HOST')) {
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
}

$dsn = 'pgsql:host='.$host.';user=postgres;password=password';

try {
$pool = new PdoAdapter($dsn);

$item = $pool->getItem('key');
$item->set('value');
$this->assertTrue($pool->save($item));
} finally {
$pdo = new \PDO($dsn);
$pdo->exec('DROP TABLE IF EXISTS cache_items');
}
}

protected function isPruned(PdoAdapter $cache, string $name): bool
Expand Down
5 changes: 4 additions & 1 deletion src/Symfony/Component/Console/Event/ConsoleCommandEvent.php
Expand Up @@ -12,7 +12,10 @@
namespace Symfony\Component\Console\Event;

/**
* Allows to do things before the command is executed, like skipping the command or changing the input.
* Allows to do things before the command is executed, like skipping the command or executing code before the command is
* going to be executed.
*
* Changing the input arguments will have no effect.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
Expand Down
4 changes: 1 addition & 3 deletions src/Symfony/Component/DomCrawler/Tests/UriResolverTest.php
Expand Up @@ -85,9 +85,7 @@ public static function provideResolverTests()
['foo', 'http://localhost?bar=1', 'http://localhost/foo'],
['foo', 'http://localhost#bar', 'http://localhost/foo'],

['foo:1', 'http://localhost', 'http://localhost/foo:1'],
['/bar:1', 'http://localhost', 'http://localhost/bar:1'],
['foo/bar:1', 'http://localhost', 'http://localhost/foo/bar:1'],
['http://', 'http://localhost', 'http://'],
];
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/DomCrawler/UriResolver.php
Expand Up @@ -33,7 +33,7 @@ public static function resolve(string $uri, ?string $baseUri): string
$uri = trim($uri);

// absolute URL?
if (\is_string(parse_url($uri, \PHP_URL_SCHEME))) {
if (null !== parse_url($uri, \PHP_URL_SCHEME)) {
return $uri;
}

Expand Down

0 comments on commit 79f841c

Please sign in to comment.