Merge branch '3.4' into 4.3
* 3.4: [Cache] fix memory leak when using PhpArrayAdapter fix parsing negative octal numbers [SecurityBundle] Properly escape regex in AddSessionDomainConstraintPass [Config] never try loading failed classes twice with ClassExistenceResource
This commit is contained in:
commit
a492e72129
@ -31,7 +31,7 @@ class AddSessionDomainConstraintPass implements CompilerPassInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sessionOptions = $container->getParameter('session.storage.options');
|
$sessionOptions = $container->getParameter('session.storage.options');
|
||||||
$domainRegexp = empty($sessionOptions['cookie_domain']) ? '%s' : sprintf('(?:%%s|(?:.+\.)?%s)', preg_quote(trim($sessionOptions['cookie_domain'], '.')));
|
$domainRegexp = empty($sessionOptions['cookie_domain']) ? '%%s' : sprintf('(?:%%%%s|(?:.+\.)?%s)', preg_quote(trim($sessionOptions['cookie_domain'], '.')));
|
||||||
|
|
||||||
if ('auto' === ($sessionOptions['cookie_secure'] ?? null)) {
|
if ('auto' === ($sessionOptions['cookie_secure'] ?? null)) {
|
||||||
$secureDomainRegexp = sprintf('{^https://%s$}i', $domainRegexp);
|
$secureDomainRegexp = sprintf('{^https://%s$}i', $domainRegexp);
|
||||||
|
@ -61,14 +61,12 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
||||||
*
|
*
|
||||||
* @param string $file The PHP file were values are cached
|
* @param string $file The PHP file were values are cached
|
||||||
* @param CacheItemPoolInterface $fallbackPool Fallback when opcache is disabled
|
* @param CacheItemPoolInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||||
*
|
*
|
||||||
* @return CacheItemPoolInterface
|
* @return CacheItemPoolInterface
|
||||||
*/
|
*/
|
||||||
public static function create($file, CacheItemPoolInterface $fallbackPool)
|
public static function create($file, CacheItemPoolInterface $fallbackPool)
|
||||||
{
|
{
|
||||||
// Shared memory is available in PHP 7.0+ with OPCache enabled
|
|
||||||
if (filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
|
|
||||||
if (!$fallbackPool instanceof AdapterInterface) {
|
if (!$fallbackPool instanceof AdapterInterface) {
|
||||||
$fallbackPool = new ProxyAdapter($fallbackPool);
|
$fallbackPool = new ProxyAdapter($fallbackPool);
|
||||||
}
|
}
|
||||||
@ -76,9 +74,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
return new static($file, $fallbackPool);
|
return new static($file, $fallbackPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $fallbackPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -42,19 +42,15 @@ class PhpArrayCache implements Psr16CacheInterface, PruneableInterface, Resettab
|
|||||||
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
||||||
*
|
*
|
||||||
* @param string $file The PHP file were values are cached
|
* @param string $file The PHP file were values are cached
|
||||||
|
* @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||||
*
|
*
|
||||||
* @return Psr16CacheInterface
|
* @return Psr16CacheInterface
|
||||||
*/
|
*/
|
||||||
public static function create($file, Psr16CacheInterface $fallbackPool)
|
public static function create($file, Psr16CacheInterface $fallbackPool)
|
||||||
{
|
{
|
||||||
// Shared memory is available in PHP 7.0+ with OPCache enabled
|
|
||||||
if (filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
|
|
||||||
return new static($file, $fallbackPool);
|
return new static($file, $fallbackPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $fallbackPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -65,6 +65,8 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
|||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
|
$this->createCachePool()->clear();
|
||||||
|
|
||||||
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
||||||
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ class PhpArrayAdapterWithFallbackTest extends AdapterTestCase
|
|||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
|
$this->createCachePool()->clear();
|
||||||
|
|
||||||
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
||||||
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,8 @@ class PhpArrayCacheTest extends CacheTestCase
|
|||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
|
$this->createSimpleCache()->clear();
|
||||||
|
|
||||||
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
||||||
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ class PhpArrayCacheWithFallbackTest extends CacheTestCase
|
|||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
|
$this->createSimpleCache()->clear();
|
||||||
|
|
||||||
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
|
||||||
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ trait PhpArrayTrait
|
|||||||
private $keys;
|
private $keys;
|
||||||
private $values;
|
private $values;
|
||||||
|
|
||||||
|
private static $valuesCache = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store an array of cached values.
|
* Store an array of cached values.
|
||||||
*
|
*
|
||||||
@ -115,6 +117,7 @@ EOF;
|
|||||||
unset($serialized, $value, $dump);
|
unset($serialized, $value, $dump);
|
||||||
|
|
||||||
@rename($tmpFile, $this->file);
|
@rename($tmpFile, $this->file);
|
||||||
|
unset(self::$valuesCache[$this->file]);
|
||||||
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
@ -127,6 +130,7 @@ EOF;
|
|||||||
$this->keys = $this->values = [];
|
$this->keys = $this->values = [];
|
||||||
|
|
||||||
$cleared = @unlink($this->file) || !file_exists($this->file);
|
$cleared = @unlink($this->file) || !file_exists($this->file);
|
||||||
|
unset(self::$valuesCache[$this->file]);
|
||||||
|
|
||||||
return $this->pool->clear() && $cleared;
|
return $this->pool->clear() && $cleared;
|
||||||
}
|
}
|
||||||
@ -136,12 +140,15 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
private function initialize()
|
private function initialize()
|
||||||
{
|
{
|
||||||
if (!file_exists($this->file)) {
|
if (isset(self::$valuesCache[$this->file])) {
|
||||||
|
$values = self::$valuesCache[$this->file];
|
||||||
|
} elseif (!file_exists($this->file)) {
|
||||||
$this->keys = $this->values = [];
|
$this->keys = $this->values = [];
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
$values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []];
|
||||||
}
|
}
|
||||||
$values = (include $this->file) ?: [[], []];
|
|
||||||
|
|
||||||
if (2 !== \count($values) || !isset($values[0], $values[1])) {
|
if (2 !== \count($values) || !isset($values[0], $values[1])) {
|
||||||
$this->keys = $this->values = [];
|
$this->keys = $this->values = [];
|
||||||
|
@ -37,7 +37,9 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
|
|||||||
public function __construct(string $resource, bool $exists = null)
|
public function __construct(string $resource, bool $exists = null)
|
||||||
{
|
{
|
||||||
$this->resource = $resource;
|
$this->resource = $resource;
|
||||||
$this->exists = $exists;
|
if (null !== $exists) {
|
||||||
|
$this->exists = [(bool) $exists, null];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,9 +67,13 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
|
|||||||
{
|
{
|
||||||
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
||||||
|
|
||||||
if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) {
|
if (null !== $exists = &self::$existsCache[$this->resource]) {
|
||||||
$exists = $exists || $loaded;
|
if ($loaded) {
|
||||||
} elseif (!$exists = $loaded) {
|
$exists = [true, null];
|
||||||
|
} elseif (0 >= $timestamp && !$exists[0] && null !== $exists[1]) {
|
||||||
|
throw new \ReflectionException($exists[1]);
|
||||||
|
}
|
||||||
|
} elseif ([false, null] === $exists = [$loaded, null]) {
|
||||||
if (!self::$autoloadLevel++) {
|
if (!self::$autoloadLevel++) {
|
||||||
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
|
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
|
||||||
}
|
}
|
||||||
@ -75,16 +81,19 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
|
|||||||
self::$autoloadedClass = ltrim($this->resource, '\\');
|
self::$autoloadedClass = ltrim($this->resource, '\\');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
$exists[0] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
$exists[1] = $e->getMessage();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
self::throwOnRequiredClass($this->resource, $e);
|
self::throwOnRequiredClass($this->resource, $e);
|
||||||
} catch (\ReflectionException $e) {
|
} catch (\ReflectionException $e) {
|
||||||
if (0 >= $timestamp) {
|
if (0 >= $timestamp) {
|
||||||
unset(self::$existsCache[1][$this->resource]);
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$exists[1] = $e->getMessage();
|
||||||
} finally {
|
} finally {
|
||||||
self::$autoloadedClass = $autoloadedClass;
|
self::$autoloadedClass = $autoloadedClass;
|
||||||
if (!--self::$autoloadLevel) {
|
if (!--self::$autoloadLevel) {
|
||||||
@ -97,7 +106,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
|
|||||||
$this->exists = $exists;
|
$this->exists = $exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->exists xor !$exists;
|
return $this->exists[0] xor !$exists[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,6 +121,16 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
|
|||||||
return ['resource', 'exists'];
|
return ['resource', 'exists'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function __wakeup()
|
||||||
|
{
|
||||||
|
if (\is_bool($this->exists)) {
|
||||||
|
$this->exists = [$this->exists, null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws a reflection exception when the passed class does not exist but is required.
|
* Throws a reflection exception when the passed class does not exist but is required.
|
||||||
*
|
*
|
||||||
@ -147,7 +166,17 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
|
|||||||
throw $previous;
|
throw $previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
$e = new \ReflectionException(sprintf('Class "%s" not found while loading "%s".', $class, self::$autoloadedClass), 0, $previous);
|
$message = sprintf('Class "%s" not found.', $class);
|
||||||
|
|
||||||
|
if (self::$autoloadedClass !== $class) {
|
||||||
|
$message = substr_replace($message, sprintf(' while loading "%s"', self::$autoloadedClass), -1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $previous) {
|
||||||
|
$message = $previous->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$e = new \ReflectionException($message, 0, $previous);
|
||||||
|
|
||||||
if (null !== $previous) {
|
if (null !== $previous) {
|
||||||
throw $e;
|
throw $e;
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Tests\Fixtures;
|
||||||
|
|
||||||
|
class FileNameMismatchOnPurpose
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('Mismatch between file name and class name.');
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Config\Tests\Resource;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Config\Resource\ClassExistenceResource;
|
use Symfony\Component\Config\Resource\ClassExistenceResource;
|
||||||
|
use Symfony\Component\Config\Tests\Fixtures\BadFileName;
|
||||||
use Symfony\Component\Config\Tests\Fixtures\BadParent;
|
use Symfony\Component\Config\Tests\Fixtures\BadParent;
|
||||||
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;
|
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;
|
||||||
|
|
||||||
@ -90,6 +91,24 @@ EOF
|
|||||||
$res->isFresh(0);
|
$res->isFresh(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBadFileName()
|
||||||
|
{
|
||||||
|
$this->expectException('ReflectionException');
|
||||||
|
$this->expectExceptionMessage('Mismatch between file name and class name.');
|
||||||
|
|
||||||
|
$res = new ClassExistenceResource(BadFileName::class, false);
|
||||||
|
$res->isFresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadFileNameBis()
|
||||||
|
{
|
||||||
|
$this->expectException('ReflectionException');
|
||||||
|
$this->expectExceptionMessage('Mismatch between file name and class name.');
|
||||||
|
|
||||||
|
$res = new ClassExistenceResource(BadFileName::class, false);
|
||||||
|
$res->isFresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
public function testConditionalClass()
|
public function testConditionalClass()
|
||||||
{
|
{
|
||||||
$res = new ClassExistenceResource(ConditionalClass::class, false);
|
$res = new ClassExistenceResource(ConditionalClass::class, false);
|
||||||
|
@ -612,11 +612,11 @@ class Inline
|
|||||||
// Optimize for returning strings.
|
// Optimize for returning strings.
|
||||||
// no break
|
// no break
|
||||||
case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]):
|
case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]):
|
||||||
switch (true) {
|
if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) {
|
||||||
case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
|
|
||||||
$scalar = str_replace('_', '', (string) $scalar);
|
$scalar = str_replace('_', '', (string) $scalar);
|
||||||
// omitting the break / return as integers are handled in the next case
|
}
|
||||||
// no break
|
|
||||||
|
switch (true) {
|
||||||
case ctype_digit($scalar):
|
case ctype_digit($scalar):
|
||||||
$raw = $scalar;
|
$raw = $scalar;
|
||||||
$cast = (int) $scalar;
|
$cast = (int) $scalar;
|
||||||
@ -626,7 +626,7 @@ class Inline
|
|||||||
$raw = $scalar;
|
$raw = $scalar;
|
||||||
$cast = (int) $scalar;
|
$cast = (int) $scalar;
|
||||||
|
|
||||||
return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
|
return '0' == $scalar[1] ? -octdec(substr($scalar, 1)) : (($raw === (string) $cast) ? $cast : $raw);
|
||||||
case is_numeric($scalar):
|
case is_numeric($scalar):
|
||||||
case Parser::preg_match(self::getHexRegex(), $scalar):
|
case Parser::preg_match(self::getHexRegex(), $scalar):
|
||||||
$scalar = str_replace('_', '', $scalar);
|
$scalar = str_replace('_', '', $scalar);
|
||||||
|
@ -720,4 +720,20 @@ class InlineTest extends TestCase
|
|||||||
$this->expectExceptionMessage('Unexpected end of line, expected one of ",}" at line 1 (near "{abc: \'def\'").');
|
$this->expectExceptionMessage('Unexpected end of line, expected one of ",}" at line 1 (near "{abc: \'def\'").');
|
||||||
Inline::parse("{abc: 'def'");
|
Inline::parse("{abc: 'def'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getTestsForOctalNumbers
|
||||||
|
*/
|
||||||
|
public function testParseOctalNumbers($expected, $yaml)
|
||||||
|
{
|
||||||
|
self::assertSame($expected, Inline::parse($yaml));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTestsForOctalNumbers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'positive octal number' => [28, '034'],
|
||||||
|
'negative octal number' => [-28, '-034'],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user