From ca566204d34dba70e16da2d2f543523c0b0a6fd2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 23 Feb 2022 18:16:36 +0100 Subject: [PATCH 01/10] Fix deprecations on PHP 8.2 --- .github/workflows/unit-tests.yml | 2 +- .../Constraints/UniqueEntityValidatorTest.php | 4 +- .../LazyProxy/PhpDumper/ProxyDumperTest.php | 1 + .../AddAnnotationsCachedReaderPass.php | 9 +- .../FrameworkExtension.php | 5 +- .../Tests/Fixtures/includes/classes.php | 1 + .../Tests/Fixtures/includes/foo.php | 1 + src/Symfony/Component/DomCrawler/Crawler.php | 4 +- .../Tests/Fixtures/MockStream/MockStream.php | 2 + .../Tests/Normalizer/Features/ObjectDummy.php | 1 + .../Tests/Constraints/ImageValidatorTest.php | 1 + .../Component/VarDumper/Dumper/HtmlDumper.php | 2 +- .../Tests/Caster/ExceptionCasterTest.php | 2 +- .../VarDumper/Tests/Caster/SplCasterTest.php | 6 +- .../VarDumper/Tests/Dumper/HtmlDumperTest.php | 2 +- .../VarDumper/Tests/Fixtures/dumb-var.php | 1 + .../VarExporter/Internal/Exporter.php | 10 +- .../VarExporter/Internal/Hydrator.php | 71 +++++++------- .../Tests/Fixtures/array-object.php | 4 +- .../Tests/Fixtures/datetime-legacy.php | 92 +++++++++++++++++++ .../VarExporter/Tests/Fixtures/datetime.php | 32 +++---- .../VarExporter/Tests/InstantiatorTest.php | 7 +- .../VarExporter/Tests/VarExporterTest.php | 21 ++++- 23 files changed, 204 insertions(+), 77 deletions(-) create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2bea6308930c9..c0af4b709c4b2 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -26,7 +26,7 @@ jobs: - php: '8.1' mode: low-deps - php: '8.2' - mode: experimental + #mode: experimental fail-fast: false runs-on: ubuntu-20.04 diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index bc0041c20b64c..3032e41f132ea 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -13,9 +13,9 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Persistence\ManagerRegistry; -use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; @@ -111,7 +111,7 @@ protected function createEntityManagerMock($repositoryMock) ->willReturn($repositoryMock) ; - $classMetadata = $this->createMock(ClassMetadata::class); + $classMetadata = $this->createMock(ClassMetadataInfo::class); $classMetadata ->expects($this->any()) ->method('hasField') diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 971f68cb74a58..b63a54b7bfbf0 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -194,6 +194,7 @@ function ($definition) { } } +#[\AllowDynamicProperties] final class DummyClass implements DummyInterface, SunnyInterface { private $ref; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php index 4f09e52bdcbd1..a0581ff21fb6f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -28,14 +29,18 @@ public function process(ContainerBuilder $container) // "annotation_reader" at build time don't get any cache foreach ($container->findTaggedServiceIds('annotations.cached_reader') as $id => $tags) { $reader = $container->getDefinition($id); + $reader->setPublic(false); $properties = $reader->getProperties(); if (isset($properties['cacheProviderBackup'])) { $provider = $properties['cacheProviderBackup']->getValues()[0]; unset($properties['cacheProviderBackup']); $reader->setProperties($properties); - $container->set($id, null); - $container->setDefinition($id, $reader->replaceArgument(1, $provider)); + $reader->replaceArgument(1, $provider); + } elseif (4 <= \count($arguments = $reader->getArguments()) && $arguments[3] instanceof ServiceClosureArgument) { + $arguments[1] = $arguments[3]->getValues()[0]; + unset($arguments[3]); + $reader->setArguments($arguments); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 0d52c684b27d0..39351b15aca9c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1463,9 +1463,10 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde $container ->getDefinition('annotations.cached_reader') + ->setPublic(true) // set to false in AddAnnotationsCachedReaderPass ->replaceArgument(2, $config['debug']) - // temporary property to lazy-reference the cache provider without using it until AddAnnotationsCachedReaderPass runs - ->setProperty('cacheProviderBackup', new ServiceClosureArgument(new Reference($cacheService))) + // reference the cache provider without using it until AddAnnotationsCachedReaderPass runs + ->addArgument(new ServiceClosureArgument(new Reference($cacheService))) ->addTag('annotations.cached_reader') ; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index bc68c74fd7776..a57561aa3337c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -56,6 +56,7 @@ public static function configureStatic1() class BarUserClass { + public $foo; public $bar; public function __construct(BarClass $bar) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php index 20bc928b9728c..c8a6b347a0029 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php @@ -4,6 +4,7 @@ class FooClass { + public $qux; public $foo; public $moo; diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index aacb94ad37f2f..462b6b1129d9e 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -1214,11 +1214,11 @@ private function convertToHtmlEntities(string $htmlContent, string $charset = 'U set_error_handler(function () { throw new \Exception(); }); try { - return mb_convert_encoding($htmlContent, 'HTML-ENTITIES', $charset); + return mb_encode_numericentity($htmlContent, [0x80, 0xFFFF, 0, 0xFFFF], $charset); } catch (\Exception|\ValueError $e) { try { $htmlContent = iconv($charset, 'UTF-8', $htmlContent); - $htmlContent = mb_convert_encoding($htmlContent, 'HTML-ENTITIES', 'UTF-8'); + $htmlContent = mb_encode_numericentity($htmlContent, [0x80, 0xFFFF, 0, 0xFFFF], 'UTF-8'); } catch (\Exception|\ValueError $e) { } diff --git a/src/Symfony/Component/Filesystem/Tests/Fixtures/MockStream/MockStream.php b/src/Symfony/Component/Filesystem/Tests/Fixtures/MockStream/MockStream.php index 3c66d8b9ac452..b9ebd6b1c6cc9 100644 --- a/src/Symfony/Component/Filesystem/Tests/Fixtures/MockStream/MockStream.php +++ b/src/Symfony/Component/Filesystem/Tests/Fixtures/MockStream/MockStream.php @@ -17,6 +17,8 @@ */ class MockStream { + public $context; + /** * Opens file or URL. * diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php index ac610f098607f..788313c2b8562 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy.php @@ -2,6 +2,7 @@ namespace Symfony\Component\Serializer\Tests\Normalizer\Features; +#[\AllowDynamicProperties] class ObjectDummy { protected $foo; diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php index 81ba1224bbfe5..14d117ddbb77d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php @@ -33,6 +33,7 @@ class ImageValidatorTest extends ConstraintValidatorTestCase protected $imageLandscape; protected $imagePortrait; protected $image4By3; + protected $image16By9; protected $imageCorrupted; protected function createValidator() diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 8409a0c741325..88e5ba9284030 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -978,7 +978,7 @@ protected function dumpLine($depth, $endOfValue = false) } $this->lastDepth = $depth; - $this->line = mb_convert_encoding($this->line, 'HTML-ENTITIES', 'UTF-8'); + $this->line = mb_encode_numericentity($this->line, [0x80, 0xFFFF, 0, 0xFFFF], 'UTF-8'); if (-1 === $depth) { AbstractDumper::dumpLine(0); diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php index e39adfa78710d..71c34f7646940 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -175,7 +175,7 @@ public function testHtmlDump() trace: { %s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php:%d - …%d + …%d } } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php index ff308aaa0ccd2..5bba4e55fc8d7 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php @@ -166,11 +166,13 @@ public function testCastObjectStorageDumpsInfo() public function testCastArrayObject() { - $var = new \ArrayObject([123]); + $var = new + #[\AllowDynamicProperties] + class([123]) extends \ArrayObject {}; $var->foo = 234; $expected = << 123 diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php index e5ad368330f1c..9921dc715a4dd 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php @@ -66,7 +66,7 @@ public function testGet() 6 => {$intMax} "str" => "d&%s;j&%s;\\n" 7 => b""" - é\\x00test\\t\\n + é\\x00test\\t\\n ing """ "[]" => [] diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php index 67e53badb6445..4ea8c8ba6ec08 100644 --- a/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php @@ -3,6 +3,7 @@ namespace Symfony\Component\VarDumper\Tests\Fixture; if (!class_exists(\Symfony\Component\VarDumper\Tests\Fixture\DumbFoo::class)) { + #[\AllowDynamicProperties] class DumbFoo { public $foo = 'foo'; diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index 4c49d87da61bf..c46eb50aa988d 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -108,7 +108,15 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount } $properties = ['SplObjectStorage' => ["\0" => $properties]]; $arrayValue = (array) $value; - } elseif ($value instanceof \Serializable || $value instanceof \__PHP_Incomplete_Class) { + } elseif ($value instanceof \Serializable + || $value instanceof \__PHP_Incomplete_Class + || $value instanceof \DatePeriod + || (\PHP_VERSION_ID >= 80200 && ( + $value instanceof \DateTimeInterface + || $value instanceof \DateTimeZone + || $value instanceof \DateInterval + )) + ) { ++$objectsCount; $objectsPool[$value] = [$id = \count($objectsPool), serialize($value), [], 0]; $value = new Reference($id); diff --git a/src/Symfony/Component/VarExporter/Internal/Hydrator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php index 364d292d916e7..5ed6bdc948e63 100644 --- a/src/Symfony/Component/VarExporter/Internal/Hydrator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -55,45 +55,14 @@ public static function hydrate($objects, $values, $properties, $value, $wakeups) public static function getHydrator($class) { - if ('stdClass' === $class) { - return self::$hydrators[$class] = static function ($properties, $objects) { - foreach ($properties as $name => $values) { - foreach ($values as $i => $v) { - $objects[$i]->$name = $v; - } - } - }; - } - - if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { - throw new ClassNotFoundException($class); - } - $classReflector = new \ReflectionClass($class); - - if (!$classReflector->isInternal()) { - return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); - } - - if ($classReflector->name !== $class) { - return self::$hydrators[$classReflector->name] ?? self::getHydrator($classReflector->name); - } - switch ($class) { - case 'ArrayIterator': - case 'ArrayObject': - $constructor = \Closure::fromCallable([$classReflector->getConstructor(), 'invokeArgs']); - - return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { + case 'stdClass': + return self::$hydrators[$class] = static function ($properties, $objects) { foreach ($properties as $name => $values) { - if ("\0" !== $name) { - foreach ($values as $i => $v) { - $objects[$i]->$name = $v; - } + foreach ($values as $i => $v) { + $objects[$i]->$name = $v; } } - foreach ($properties["\0"] ?? [] as $i => $v) { - $constructor($objects[$i], $v); - } }; case 'ErrorException': @@ -122,6 +91,38 @@ public static function getHydrator($class) }; } + if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { + throw new ClassNotFoundException($class); + } + $classReflector = new \ReflectionClass($class); + + switch ($class) { + case 'ArrayIterator': + case 'ArrayObject': + $constructor = \Closure::fromCallable([$classReflector->getConstructor(), 'invokeArgs']); + + return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { + foreach ($properties as $name => $values) { + if ("\0" !== $name) { + foreach ($values as $i => $v) { + $objects[$i]->$name = $v; + } + } + } + foreach ($properties["\0"] ?? [] as $i => $v) { + $constructor($objects[$i], $v); + } + }; + } + + if (!$classReflector->isInternal()) { + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); + } + + if ($classReflector->name !== $class) { + return self::$hydrators[$classReflector->name] ?? self::getHydrator($classReflector->name); + } + $propertySetters = []; foreach ($classReflector->getProperties() as $propertyReflector) { if (!$propertyReflector->isStatic()) { diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php index 4e2d4d13a3d89..22fb5c9b247d9 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php @@ -2,8 +2,8 @@ return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ - clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['ArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('ArrayObject')), - clone $p['ArrayObject'], + clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Symfony\\Component\\VarExporter\\Tests\\ArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Symfony\\Component\\VarExporter\\Tests\\ArrayObject')), + clone ($p['ArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('ArrayObject')), ], null, [], diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php new file mode 100644 index 0000000000000..7b217c5fb21b0 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime-legacy.php @@ -0,0 +1,92 @@ + 'O:10:"DatePeriod":6:{s:5:"start";O:8:"DateTime":3:{s:4:"date";s:26:"2012-07-01 00:00:00.000000";s:13:"timezone_type";i:1;s:8:"timezone";s:6:"+00:00";}s:7:"current";N;s:3:"end";N;s:8:"interval";O:12:"DateInterval":16:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";b:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}s:11:"recurrences";i:5;s:18:"include_start_date";b:1;}', + ]), + null, + [ + 'stdClass' => [ + 'date' => [ + '1970-01-01 00:00:00.000000', + '1970-01-01 00:00:00.000000', + ], + 'timezone_type' => [ + 1, + 1, + 3, + ], + 'timezone' => [ + '+00:00', + '+00:00', + 'Europe/Paris', + ], + 'y' => [ + 3 => 0, + ], + 'm' => [ + 3 => 0, + ], + 'd' => [ + 3 => 7, + ], + 'h' => [ + 3 => 0, + ], + 'i' => [ + 3 => 0, + ], + 's' => [ + 3 => 0, + ], + 'f' => [ + 3 => 0.0, + ], + 'weekday' => [ + 3 => 0, + ], + 'weekday_behavior' => [ + 3 => 0, + ], + 'first_last_day_of' => [ + 3 => 0, + ], + 'invert' => [ + 3 => 0, + ], + 'days' => [ + 3 => false, + ], + 'special_type' => [ + 3 => 0, + ], + 'special_amount' => [ + 3 => 0, + ], + 'have_weekday_relative' => [ + 3 => 0, + ], + 'have_special_relative' => [ + 3 => 0, + ], + ], + ], + [ + $o[0], + $o[1], + $o[2], + $o[3], + $o[4], + ], + [ + 1 => 0, + 1, + 2, + 3, + ] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php index 1c916458c4f1a..1de8fa03f0919 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php @@ -1,25 +1,21 @@ [ - 'date' => [ - '1970-01-01 00:00:00.000000', - ], - 'timezone_type' => [ - 1, - ], - 'timezone' => [ - '+00:00', - ], - ], + $o[0], + $o[1], + $o[2], + $o[3], + $o[4], ], - $o[0], - [ - 1 => 0, - ] + [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php b/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php index 744e576c0a0c9..cbd223642320b 100644 --- a/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php +++ b/src/Symfony/Component/VarExporter/Tests/InstantiatorTest.php @@ -53,16 +53,16 @@ public function testInstantiate() $expected = [ "\0".__NAMESPACE__."\Bar\0priv" => 123, "\0".__NAMESPACE__."\Foo\0priv" => 234, + 'dyn' => 345, ]; - $actual = (array) Instantiator::instantiate(Bar::class, ['priv' => 123], [Foo::class => ['priv' => 234]]); + $actual = (array) Instantiator::instantiate(Bar::class, ['dyn' => 345, 'priv' => 123], [Foo::class => ['priv' => 234]]); ksort($actual); $this->assertSame($expected, $actual); - $e = Instantiator::instantiate('Exception', ['foo' => 123, 'trace' => [234]]); + $e = Instantiator::instantiate('Exception', ['trace' => [234]]); - $this->assertSame(123, $e->foo); $this->assertSame([234], $e->getTrace()); } } @@ -72,6 +72,7 @@ class Foo private $priv; } +#[\AllowDynamicProperties] class Bar extends Foo { private $priv; diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index fffccd4b53db1..f87e4e9b01d1e 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -95,7 +95,9 @@ public function testExport(string $testName, $value, bool $staticValueExpected = $dump = "= 70406 || !\in_array($testName, ['array-object', 'array-iterator', 'array-object-custom', 'spl-object-storage', 'final-array-iterator', 'final-error'], true)) { + if (\PHP_VERSION_ID < 80200 && 'datetime' === $testName) { + $fixtureFile = __DIR__.'/Fixtures/'.$testName.'-legacy.php'; + } elseif (\PHP_VERSION_ID >= 70406 || !\in_array($testName, ['array-object', 'array-iterator', 'array-object-custom', 'spl-object-storage', 'final-array-iterator', 'final-error'], true)) { $fixtureFile = __DIR__.'/Fixtures/'.$testName.'.php'; } elseif (\PHP_VERSION_ID < 70400) { $fixtureFile = __DIR__.'/Fixtures/'.$testName.'-legacy.php'; @@ -127,9 +129,15 @@ public function provideExport() yield ['bool', true, true]; yield ['simple-array', [123, ['abc']], true]; yield ['partially-indexed-array', [5 => true, 1 => true, 2 => true, 6 => true], true]; - yield ['datetime', \DateTime::createFromFormat('U', 0)]; - - $value = new \ArrayObject(); + yield ['datetime', [ + \DateTime::createFromFormat('U', 0), + \DateTimeImmutable::createFromFormat('U', 0), + new \DateTimeZone('Europe/Paris'), + new \DateInterval('P7D'), + new \DatePeriod('R4/2012-07-01T00:00:00Z/P7D'), + ]]; + + $value = \PHP_VERSION_ID >= 70406 ? new ArrayObject() : new \ArrayObject(); $value[0] = 1; $value->foo = new \ArrayObject(); $value[1] = $value; @@ -436,3 +444,8 @@ public function unserialize($ser) throw new \BadMethodCallException(); } } + +#[\AllowDynamicProperties] +class ArrayObject extends \ArrayObject +{ +} From 05868b29c1b687d2a2da5498facc703bff82bfbb Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 28 Feb 2022 08:57:26 +0100 Subject: [PATCH 02/10] Bump Symfony version to 4.4.39 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 74a8e904ac9e9..5676a967ac8d8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '4.4.38'; - public const VERSION_ID = 40438; + public const VERSION = '4.4.39-DEV'; + public const VERSION_ID = 40439; public const MAJOR_VERSION = 4; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 38; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 39; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2022'; public const END_OF_LIFE = '11/2023'; From c555813c3514be308f40e1381a6fc6cb6eb98cfb Mon Sep 17 00:00:00 2001 From: Xavier Coureau Date: Mon, 28 Feb 2022 12:53:56 +0100 Subject: [PATCH 03/10] [WebProfilerBundle] Fixes HTML syntax regression introduced by #44570 --- .../WebProfilerBundle/Resources/views/Profiler/base.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig index eee81b8311c76..9426753b00e57 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig @@ -8,13 +8,13 @@ {% block head %} - + {{ include('@WebProfiler/Profiler/profiler.css.twig') }} {% endblock %} - + document.body.classList.add( localStorage.getItem('symfony/profiler/theme') || (matchMedia('(prefers-color-scheme: dark)').matches ? 'theme-dark' : 'theme-light'), localStorage.getItem('symfony/profiler/width') || 'width-normal' From 721996a14439c15e81e1d8fec96e2c1ce4f1533a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 28 Feb 2022 14:17:32 +0100 Subject: [PATCH 04/10] [HttpClient] fix checking for unset property on PHP <= 7.1.4 --- src/Symfony/Component/HttpClient/HttpClientTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index de1689490e623..57d48aade8db2 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -189,7 +189,7 @@ private static function mergeDefaultOptions(array $options, array $defaultOption $options += $defaultOptions; - foreach (self::$emptyDefaults ?? [] as $k => $v) { + foreach (isset(self::$emptyDefaults) ? self::$emptyDefaults : [] as $k => $v) { if (!isset($options[$k])) { $options[$k] = $v; } From 4453bdb74959821f2168ecaade0ff44bd54a7a56 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 1 Mar 2022 13:09:24 +0100 Subject: [PATCH 05/10] [FrameworkBundle] Fix resetting container between tests --- src/Symfony/Bundle/FrameworkBundle/Client.php | 7 +------ .../FrameworkBundle/Test/KernelTestCase.php | 17 ++++++----------- .../FrameworkBundle/Tests/KernelBrowserTest.php | 10 ++++++++++ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Client.php b/src/Symfony/Bundle/FrameworkBundle/Client.php index a27a7141dfba2..97b19cce3a499 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Client.php +++ b/src/Symfony/Bundle/FrameworkBundle/Client.php @@ -19,7 +19,6 @@ use Symfony\Component\HttpKernel\HttpKernelBrowser; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile; -use Symfony\Contracts\Service\ResetInterface; /** * Client simulates a browser and makes requests to a Kernel object. @@ -117,12 +116,8 @@ protected function doRequest($request) // avoid shutting down the Kernel if no request has been performed yet // WebTestCase::createClient() boots the Kernel but do not handle a request if ($this->hasPerformedRequest && $this->reboot) { - $container = $this->kernel->getContainer(); + $this->kernel->boot(); $this->kernel->shutdown(); - - if ($container instanceof ResetInterface) { - $container->reset(); - } } else { $this->hasPerformedRequest = true; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index b87018bbd6b3f..a65a17b4c53e2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\KernelInterface; -use Symfony\Contracts\Service\ResetInterface; /** * KernelTestCase is the base class for tests needing a Kernel. @@ -39,8 +38,6 @@ abstract class KernelTestCase extends TestCase protected static $booted = false; - private static $kernelContainer; - private function doTearDown() { static::ensureKernelShutdown(); @@ -77,11 +74,12 @@ protected static function bootKernel(array $options = []) { static::ensureKernelShutdown(); - static::$kernel = static::createKernel($options); - static::$kernel->boot(); + $kernel = static::createKernel($options); + $kernel->boot(); + self::$kernel = $kernel; static::$booted = true; - self::$kernelContainer = $container = static::$kernel->getContainer(); + $container = static::$kernel->getContainer(); static::$container = $container->has('test.service_container') ? $container->get('test.service_container') : $container; return static::$kernel; @@ -132,14 +130,11 @@ protected static function createKernel(array $options = []) protected static function ensureKernelShutdown() { if (null !== static::$kernel) { + static::$kernel->boot(); static::$kernel->shutdown(); static::$booted = false; } - if (self::$kernelContainer instanceof ResetInterface) { - self::$kernelContainer->reset(); - } - - static::$container = self::$kernelContainer = null; + static::$container = null; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php index 96eaaea7612af..404a239b51282 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php @@ -51,6 +51,16 @@ public function testEnableRebootKernel() $client->request('GET', '/'); } + public function testRequestAfterKernelShutdownAndPerformedRequest() + { + $this->expectNotToPerformAssertions(); + + $client = static::createClient(['test_case' => 'TestServiceContainer']); + $client->request('GET', '/'); + static::ensureKernelShutdown(); + $client->request('GET', '/'); + } + private function getKernelMock() { $mock = $this->getMockBuilder($this->getKernelClass()) From f4a208915ef1e0e7d6eee72653d04c52cf7b261f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 2 Mar 2022 11:44:57 +0100 Subject: [PATCH 06/10] [HttpKernel] Guard against bad profile data --- .../HttpKernel/Profiler/FileProfilerStorage.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php index 1ef58dc74cb92..d729994c1f0cc 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -123,7 +123,11 @@ public function read($token): ?Profile $file = 'compress.zlib://'.$file; } - return $this->createProfileFromData($token, unserialize(file_get_contents($file))); + if (!$data = unserialize(file_get_contents($file))) { + return null; + } + + return $this->createProfileFromData($token, $data); } /** @@ -297,7 +301,11 @@ protected function createProfileFromData($token, $data, $parent = null) $file = 'compress.zlib://'.$file; } - $profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile)); + if (!$childData = unserialize(file_get_contents($file))) { + continue; + } + + $profile->addChild($this->createProfileFromData($token, $childData, $profile)); } return $profile; From b909acf2eecc36624cd1277c1aa863bf4cb01b52 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 4 Mar 2022 07:24:56 +0100 Subject: [PATCH 07/10] [HttpFoundation] Fix PHP 8.1 deprecation in isNotModified --- src/Symfony/Component/HttpFoundation/Response.php | 3 +-- .../HttpFoundation/Tests/ResponseTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index bd990e9c04ae7..bfdc1b24a789a 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -1079,8 +1079,7 @@ public function isNotModified(Request $request): bool $lastModified = $this->headers->get('Last-Modified'); $modifiedSince = $request->headers->get('If-Modified-Since'); - if ($ifNoneMatchEtags = $request->getETags()) { - $etag = $this->getEtag(); + if (($ifNoneMatchEtags = $request->getETags()) && (null !== $etag = $this->getEtag())) { if (0 == strncmp($etag, 'W/', 2)) { $etag = substr($etag, 2); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 473406287dd09..734961b80e88b 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -284,6 +284,20 @@ public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified() $this->assertFalse($response->isNotModified($request)); } + public function testIfNoneMatchWithoutETag() + { + $request = new Request(); + $request->headers->set('If-None-Match', 'randomly_generated_etag'); + + $this->assertFalse((new Response())->isNotModified($request)); + + // Test wildcard + $request = new Request(); + $request->headers->set('If-None-Match', '*'); + + $this->assertFalse((new Response())->isNotModified($request)); + } + public function testIsValidateable() { $response = new Response('', 200, ['Last-Modified' => $this->createDateTimeOneHourAgo()->format(\DATE_RFC2822)]); From a16383d692c098a444c5b4c20cc73d03fe4d4b38 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 5 Mar 2022 22:04:43 +0100 Subject: [PATCH 08/10] Update CHANGELOG for 4.4.39 --- CHANGELOG-4.4.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md index af2b1a47f2ce5..29cc4c6552b90 100644 --- a/CHANGELOG-4.4.md +++ b/CHANGELOG-4.4.md @@ -7,6 +7,15 @@ in 4.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1 +* 4.4.39 (2022-03-05) + + * bug #45631 [HttpFoundation] Fix PHP 8.1 deprecation in `Response::isNotModified` (HypeMC) + * bug #45610 [HttpKernel] Guard against bad profile data (nicolas-grekas) + * bug #45532 Fix deprecations on PHP 8.2 (nicolas-grekas) + * bug #45595 [FrameworkBundle] Fix resetting container between tests (nicolas-grekas) + * bug #45585 [HttpClient] fix checking for unset property on PHP <= 7.1.4 (nicolas-grekas) + * bug #45583 [WebProfilerBundle] Fixes HTML syntax regression introduced by #44570 (xavismeh) + * 4.4.38 (2022-02-28) * bug #44570 [WebProfilerBundle] add nonces to profiler (garak) From f7b5636d15dcb7f22dfb19522dd9195d725d9d17 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 5 Mar 2022 22:04:53 +0100 Subject: [PATCH 09/10] Update CONTRIBUTORS for 4.4.39 --- CONTRIBUTORS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c8eb4764f6c53..e2781d32f4a30 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -78,8 +78,8 @@ The Symfony Connect username in parenthesis allows to get more information - Henrik Bjørnskov (henrikbjorn) - Antoine M (amakdessi) - Miha Vrhovnik - - Diego Saint Esteben (dii3g0) - Mathieu Piot (mpiot) + - Diego Saint Esteben (dii3g0) - Konstantin Kudryashov (everzet) - Vladimir Reznichenko (kalessil) - Bilal Amarni (bamarni) @@ -110,13 +110,13 @@ The Symfony Connect username in parenthesis allows to get more information - Luis Cordova (cordoval) - Daniel Holmes (dholmes) - Sebastiaan Stok (sstok) + - Alexandre Daubois (alexandre-daubois) - HypeMC (hypemc) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - John Wards (johnwards) - Tomas Norkūnas (norkunas) - - Alexandre Daubois (alexandre-daubois) - Julien Falque (julienfalque) - Baptiste Clavié (talus) - Massimiliano Arione (garak) @@ -2723,6 +2723,7 @@ The Symfony Connect username in parenthesis allows to get more information - Cyrille Jouineau (tuxosaurus) - Vladimir Chernyshev (volch) - Wim Godden (wimg) + - Xav` (xavismeh) - Yorkie Chadwick (yorkie76) - Maxime Aknin (3m1x4m) - Geordie From c82721799f46dc7222775a56c72c2ff53d7bb03e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 5 Mar 2022 22:04:55 +0100 Subject: [PATCH 10/10] Update VERSION for 4.4.39 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 5676a967ac8d8..e4903bfff60aa 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '4.4.39-DEV'; + public const VERSION = '4.4.39'; public const VERSION_ID = 40439; public const MAJOR_VERSION = 4; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 39; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2022'; public const END_OF_LIFE = '11/2023';