- *
- * @final since Symfony 4.3
*/
-class Response
+final class Response
{
- /** @internal */
- protected $content;
- /** @internal */
- protected $status;
- /** @internal */
- protected $headers;
+ private $content;
+ private $status;
+ private $headers;
/**
* The headers array is a set of key/value pairs. If a header is present multiple times
@@ -45,7 +40,7 @@ public function __construct(string $content = '', int $status = 200, array $head
*
* @return string The response with headers and content
*/
- public function __toString()
+ public function __toString(): string
{
$headers = '';
foreach ($this->headers as $name => $value) {
@@ -61,47 +56,16 @@ public function __toString()
return $headers."\n".$this->content;
}
- /**
- * Returns the build header line.
- *
- * @param string $name The header name
- * @param string $value The header value
- *
- * @return string The built header line
- *
- * @deprecated since Symfony 4.3
- */
- protected function buildHeader($name, $value)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
-
- return sprintf("%s: %s\n", $name, $value);
- }
-
/**
* Gets the response content.
*
* @return string The response content
*/
- public function getContent()
+ public function getContent(): string
{
return $this->content;
}
- /**
- * Gets the response status code.
- *
- * @return int The response status code
- *
- * @deprecated since Symfony 4.3, use getStatusCode() instead
- */
- public function getStatus()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.3, use getStatusCode() instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->status;
- }
-
public function getStatusCode(): int
{
return $this->status;
@@ -112,7 +76,7 @@ public function getStatusCode(): int
*
* @return array The response headers
*/
- public function getHeaders()
+ public function getHeaders(): array
{
return $this->headers;
}
@@ -120,12 +84,9 @@ public function getHeaders()
/**
* Gets a response header.
*
- * @param string $header The header name
- * @param bool $first Whether to return the first value or all header values
- *
* @return string|array The first header value if $first is true, an array of values otherwise
*/
- public function getHeader($header, $first = true)
+ public function getHeader(string $header, bool $first = true)
{
$normalizedHeader = str_replace('-', '_', strtolower($header));
foreach ($this->headers as $key => $value) {
diff --git a/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php b/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php
index 79d47bebe17ec..e0e3fb2ca8f28 100644
--- a/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php
+++ b/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\BrowserKit\Tests;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\BrowserKit\Client;
use Symfony\Component\BrowserKit\CookieJar;
use Symfony\Component\BrowserKit\History;
use Symfony\Component\BrowserKit\Response;
@@ -24,16 +23,6 @@ public function getBrowser(array $server = [], History $history = null, CookieJa
return new TestClient($server, $history, $cookieJar);
}
- /**
- * @group legacy
- */
- public function testAbstractBrowserIsAClient()
- {
- $browser = $this->getBrowser();
-
- $this->assertInstanceOf(Client::class, $browser);
- }
-
public function testGetHistory()
{
$client = $this->getBrowser([], $history = new History());
@@ -54,12 +43,11 @@ public function testGetRequest()
$this->assertEquals('http://example.com/', $client->getRequest()->getUri(), '->getCrawler() returns the Request of the last request');
}
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\BrowserKit\Tests\%s::getRequest()" method before the "request()" one is deprecated since Symfony 4.1 and will throw an exception in 5.0.
- */
public function testGetRequestNull()
{
+ $this->expectException('Symfony\Component\BrowserKit\Exception\BadMethodCallException');
+ $this->expectExceptionMessage('The "request()" method must be called before "Symfony\\Component\\BrowserKit\\AbstractBrowser::getRequest()".');
+
$client = $this->getBrowser();
$this->assertNull($client->getRequest());
}
@@ -92,22 +80,20 @@ public function testGetResponse()
$this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getResponse(), '->getCrawler() returns the Response of the last request');
}
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\BrowserKit\Tests\%s::getResponse()" method before the "request()" one is deprecated since Symfony 4.1 and will throw an exception in 5.0.
- */
public function testGetResponseNull()
{
+ $this->expectException('Symfony\Component\BrowserKit\Exception\BadMethodCallException');
+ $this->expectExceptionMessage('The "request()" method must be called before "Symfony\\Component\\BrowserKit\\AbstractBrowser::getResponse()".');
+
$client = $this->getBrowser();
$this->assertNull($client->getResponse());
}
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\BrowserKit\Tests\%s::getInternalResponse()" method before the "request()" one is deprecated since Symfony 4.1 and will throw an exception in 5.0.
- */
public function testGetInternalResponseNull()
{
+ $this->expectException('Symfony\Component\BrowserKit\Exception\BadMethodCallException');
+ $this->expectExceptionMessage('The "request()" method must be called before "Symfony\\Component\\BrowserKit\\AbstractBrowser::getInternalResponse()".');
+
$client = $this->getBrowser();
$this->assertNull($client->getInternalResponse());
}
@@ -130,12 +116,11 @@ public function testGetCrawler()
$this->assertSame($crawler, $client->getCrawler(), '->getCrawler() returns the Crawler of the last request');
}
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\BrowserKit\Tests\%s::getCrawler()" method before the "request()" one is deprecated since Symfony 4.1 and will throw an exception in 5.0.
- */
public function testGetCrawlerNull()
{
+ $this->expectException('Symfony\Component\BrowserKit\Exception\BadMethodCallException');
+ $this->expectExceptionMessage('The "request()" method must be called before "Symfony\\Component\\BrowserKit\\AbstractBrowser::getCrawler()".');
+
$client = $this->getBrowser();
$this->assertNull($client->getCrawler());
}
@@ -835,24 +820,12 @@ public function testInternalRequest()
$this->assertInstanceOf('Symfony\Component\BrowserKit\Request', $client->getInternalRequest());
}
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\BrowserKit\Tests\%s::getInternalRequest()" method before the "request()" one is deprecated since Symfony 4.1 and will throw an exception in 5.0.
- */
public function testInternalRequestNull()
{
+ $this->expectException('Symfony\Component\BrowserKit\Exception\BadMethodCallException');
+ $this->expectExceptionMessage('The "request()" method must be called before "Symfony\\Component\\BrowserKit\\AbstractBrowser::getInternalRequest()".');
+
$client = $this->getBrowser();
$this->assertNull($client->getInternalRequest());
}
-
- /**
- * @group legacy
- * @expectedDeprecation The "Symfony\Component\BrowserKit\Tests\ClassThatInheritClient::submit()" method will have a new "array $serverParameters = []" argument in version 5.0, not defining it is deprecated since Symfony 4.2.
- */
- public function testInheritedClassCallSubmitWithTwoArguments()
- {
- $clientChild = new ClassThatInheritClient();
- $clientChild->setNextResponse(new Response(''));
- $clientChild->submit($clientChild->request('GET', 'http://www.example.com/foo/foobar')->filter('input')->form());
- }
}
diff --git a/src/Symfony/Component/BrowserKit/Tests/ClassThatInheritClient.php b/src/Symfony/Component/BrowserKit/Tests/ClassThatInheritClient.php
deleted file mode 100644
index 9b445472042fc..0000000000000
--- a/src/Symfony/Component/BrowserKit/Tests/ClassThatInheritClient.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\BrowserKit\Tests;
-
-use Symfony\Component\BrowserKit\AbstractBrowser;
-use Symfony\Component\BrowserKit\Response;
-use Symfony\Component\DomCrawler\Crawler;
-use Symfony\Component\DomCrawler\Form as DomCrawlerForm;
-
-class ClassThatInheritClient extends AbstractBrowser
-{
- protected $nextResponse = null;
-
- public function setNextResponse(Response $response)
- {
- $this->nextResponse = $response;
- }
-
- protected function doRequest($request): Response
- {
- if (null === $this->nextResponse) {
- return new Response();
- }
-
- $response = $this->nextResponse;
- $this->nextResponse = null;
-
- return $response;
- }
-
- /**
- * @param array $serverParameters
- */
- public function submit(DomCrawlerForm $form, array $values = []/*, array $serverParameters = []*/): Crawler
- {
- return parent::submit($form, $values);
- }
-}
diff --git a/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php b/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php
index bac629f732bc2..d70433549bec9 100644
--- a/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php
+++ b/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php
@@ -22,15 +22,6 @@ public function testGetUri()
$this->assertEquals('foo', $response->getContent(), '->getContent() returns the content of the response');
}
- /**
- * @group legacy
- */
- public function testGetStatus()
- {
- $response = new Response('foo', 304);
- $this->assertEquals('304', $response->getStatus(), '->getStatus() returns the status of the response');
- }
-
public function testGetStatusCode()
{
$response = new Response('foo', 304);
diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json
index c51477105776a..b04b75ed21506 100644
--- a/src/Symfony/Component/BrowserKit/composer.json
+++ b/src/Symfony/Component/BrowserKit/composer.json
@@ -16,14 +16,14 @@
}
],
"require": {
- "php": "^7.1.3",
- "symfony/dom-crawler": "^3.4|^4.0|^5.0"
+ "php": "^7.2.5",
+ "symfony/dom-crawler": "^4.4|^5.0"
},
"require-dev": {
- "symfony/css-selector": "^3.4|^4.0|^5.0",
- "symfony/http-client": "^4.3|^5.0",
- "symfony/mime": "^4.3|^5.0",
- "symfony/process": "^3.4|^4.0|^5.0"
+ "symfony/css-selector": "^4.4|^5.0",
+ "symfony/http-client": "^4.4|^5.0",
+ "symfony/mime": "^4.4|^5.0",
+ "symfony/process": "^4.4|^5.0"
},
"suggest": {
"symfony/process": ""
@@ -37,7 +37,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index 98b42a4915255..56e08f9607f82 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -99,14 +99,9 @@ static function ($deferred, $namespace, &$expiredIds) use ($getId) {
*
* Using ApcuAdapter makes system caches compatible with read-only filesystems.
*
- * @param string $namespace
- * @param int $defaultLifetime
- * @param string $version
- * @param string $directory
- *
* @return AdapterInterface
*/
- public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
+ public static function createSystemCache(string $namespace, int $defaultLifetime, string $version, string $directory, LoggerInterface $logger = null)
{
$opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, true);
if (null !== $logger) {
@@ -117,7 +112,7 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
return $opcache;
}
- $apcu = new ApcuAdapter($namespace, (int) $defaultLifetime / 5, $version);
+ $apcu = new ApcuAdapter($namespace, $defaultLifetime / 5, $version);
if ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
$apcu->setLogger(new NullLogger());
} elseif (null !== $logger) {
@@ -127,17 +122,17 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
return new ChainAdapter([$apcu, $opcache]);
}
- public static function createConnection($dsn, array $options = [])
+ public static function createConnection(string $dsn, array $options = [])
{
- if (!\is_string($dsn)) {
- throw new InvalidArgumentException(sprintf('The %s() method expect argument #1 to be string, %s given.', __METHOD__, \gettype($dsn)));
- }
if (0 === strpos($dsn, 'redis:') || 0 === strpos($dsn, 'rediss:')) {
return RedisAdapter::createConnection($dsn, $options);
}
if (0 === strpos($dsn, 'memcached:')) {
return MemcachedAdapter::createConnection($dsn, $options);
}
+ if (0 === strpos($dsn, 'couchbase:')) {
+ return CouchbaseBucketAdapter::createConnection($dsn, $options);
+ }
throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn));
}
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php
index 10aca3bdf8db7..a1d244043d7b7 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php
@@ -128,7 +128,7 @@ static function ($deferred, &$expiredIds) use ($getId, $tagPrefix) {
*
* @return array The identifiers that failed to be cached or a boolean stating if caching succeeded or not
*/
- abstract protected function doSave(array $values, ?int $lifetime, array $addTagData = [], array $removeTagData = []): array;
+ abstract protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array;
/**
* Removes multiple items from the pool and their corresponding tags.
diff --git a/src/Symfony/Component/Cache/Adapter/AdapterInterface.php b/src/Symfony/Component/Cache/Adapter/AdapterInterface.php
index c40ae42b55fa9..99c5cd1f9f49e 100644
--- a/src/Symfony/Component/Cache/Adapter/AdapterInterface.php
+++ b/src/Symfony/Component/Cache/Adapter/AdapterInterface.php
@@ -38,9 +38,7 @@ public function getItems(array $keys = []);
/**
* {@inheritdoc}
*
- * @param string $prefix
- *
* @return bool
*/
- public function clear(/*string $prefix = ''*/);
+ public function clear(string $prefix = '');
}
diff --git a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php
index 7db3956588026..62ba4d9a82663 100644
--- a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php
@@ -11,17 +11,112 @@
namespace Symfony\Component\Cache\Adapter;
-use Symfony\Component\Cache\Traits\ApcuTrait;
+use Symfony\Component\Cache\CacheItem;
+use Symfony\Component\Cache\Exception\CacheException;
+/**
+ * @author Nicolas Grekas
+ */
class ApcuAdapter extends AbstractAdapter
{
- use ApcuTrait;
-
/**
* @throws CacheException if APCu is not enabled
*/
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null)
{
- $this->init($namespace, $defaultLifetime, $version);
+ if (!static::isSupported()) {
+ throw new CacheException('APCu is not enabled');
+ }
+ if ('cli' === \PHP_SAPI) {
+ ini_set('apc.use_request_time', 0);
+ }
+ parent::__construct($namespace, $defaultLifetime);
+
+ if (null !== $version) {
+ CacheItem::validateKey($version);
+
+ if (!apcu_exists($version.'@'.$namespace)) {
+ $this->doClear($namespace);
+ apcu_add($version.'@'.$namespace, null);
+ }
+ }
+ }
+
+ public static function isSupported()
+ {
+ return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch(array $ids)
+ {
+ $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
+ try {
+ $values = [];
+ foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) {
+ if (null !== $v || $ok) {
+ $values[$k] = $v;
+ }
+ }
+
+ return $values;
+ } catch (\Error $e) {
+ throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
+ } finally {
+ ini_set('unserialize_callback_func', $unserializeCallbackHandler);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doHave(string $id)
+ {
+ return apcu_exists($id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doClear(string $namespace)
+ {
+ return isset($namespace[0]) && class_exists('APCuIterator', false) && ('cli' !== \PHP_SAPI || filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN))
+ ? apcu_delete(new \APCuIterator(sprintf('/^%s/', preg_quote($namespace, '/')), APC_ITER_KEY))
+ : apcu_clear_cache();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete(array $ids)
+ {
+ foreach ($ids as $id) {
+ apcu_delete($id);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave(array $values, int $lifetime)
+ {
+ try {
+ if (false === $failures = apcu_store($values, null, $lifetime)) {
+ $failures = $values;
+ }
+
+ return array_keys($failures);
+ } catch (\Throwable $e) {
+ if (1 === \count($values)) {
+ // Workaround https://github.com/krakjoe/apcu/issues/170
+ apcu_delete(key($values));
+ }
+
+ throw $e;
+ }
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
index d93dcbd79ca11..ffec7d3d2c538 100644
--- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
@@ -13,26 +13,46 @@
use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Cache\CacheItem;
+use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface;
-use Symfony\Component\Cache\Traits\ArrayTrait;
use Symfony\Contracts\Cache\CacheInterface;
/**
+ * An in-memory cache storage.
+ *
+ * Acts as a least-recently-used (LRU) storage when configured with a maximum number of items.
+ *
* @author Nicolas Grekas
*/
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
{
- use ArrayTrait;
+ use LoggerAwareTrait;
+ private $storeSerialized;
+ private $values = [];
+ private $expiries = [];
private $createCacheItem;
+ private $maxLifetime;
+ private $maxItems;
/**
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
*/
- public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
+ public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true, int $maxLifetime = 0, int $maxItems = 0)
{
+ if (0 > $maxLifetime) {
+ throw new InvalidArgumentException(sprintf('Argument $maxLifetime must be a positive integer, %d passed.', $maxLifetime));
+ }
+
+ if (0 > $maxItems) {
+ throw new InvalidArgumentException(sprintf('Argument $maxItems must be a positive integer, %d passed.', $maxItems));
+ }
+
$this->storeSerialized = $storeSerialized;
+ $this->maxLifetime = $maxLifetime;
+ $this->maxItems = $maxItems;
$this->createCacheItem = \Closure::bind(
static function ($key, $value, $isHit) use ($defaultLifetime) {
$item = new CacheItem();
@@ -65,13 +85,48 @@ public function get(string $key, callable $callback, float $beta = null, array &
return $item->get();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function delete(string $key): bool
+ {
+ return $this->deleteItem($key);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function hasItem($key)
+ {
+ if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
+ if ($this->maxItems) {
+ // Move the item last in the storage
+ $value = $this->values[$key];
+ unset($this->values[$key]);
+ $this->values[$key] = $value;
+ }
+
+ return true;
+ }
+ CacheItem::validateKey($key);
+
+ return isset($this->expiries[$key]) && !$this->deleteItem($key);
+ }
+
/**
* {@inheritdoc}
*/
public function getItem($key)
{
if (!$isHit = $this->hasItem($key)) {
- $this->values[$key] = $value = null;
+ $value = null;
+
+ if (!$this->maxItems) {
+ // Track misses in non-LRU mode only
+ $this->values[$key] = null;
+ }
} else {
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
}
@@ -94,6 +149,21 @@ public function getItems(array $keys = [])
return $this->generateItems($keys, microtime(true), $this->createCacheItem);
}
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function deleteItem($key)
+ {
+ if (!\is_string($key) || !isset($this->expiries[$key])) {
+ CacheItem::validateKey($key);
+ }
+ unset($this->values[$key], $this->expiries[$key]);
+
+ return true;
+ }
+
/**
* {@inheritdoc}
*
@@ -123,7 +193,9 @@ public function save(CacheItemInterface $item)
$value = $item["\0*\0value"];
$expiry = $item["\0*\0expiry"];
- if (null !== $expiry && $expiry <= microtime(true)) {
+ $now = microtime(true);
+
+ if (null !== $expiry && $expiry <= $now) {
$this->deleteItem($key);
return true;
@@ -132,7 +204,23 @@ public function save(CacheItemInterface $item)
return false;
}
if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) {
- $expiry = microtime(true) + $item["\0*\0defaultLifetime"];
+ $expiry = $item["\0*\0defaultLifetime"];
+ $expiry = $now + ($expiry > ($this->maxLifetime ?: $expiry) ? $this->maxLifetime : $expiry);
+ } elseif ($this->maxLifetime && (null === $expiry || $expiry > $now + $this->maxLifetime)) {
+ $expiry = $now + $this->maxLifetime;
+ }
+
+ if ($this->maxItems) {
+ unset($this->values[$key]);
+
+ // Iterate items and vacuum expired ones while we are at it
+ foreach ($this->values as $k => $v) {
+ if ($this->expiries[$k] > $now && \count($this->values) < $this->maxItems) {
+ break;
+ }
+
+ unset($this->values[$k], $this->expiries[$k]);
+ }
}
$this->values[$key] = $value;
@@ -163,9 +251,143 @@ public function commit()
/**
* {@inheritdoc}
+ *
+ * @return bool
*/
- public function delete(string $key): bool
+ public function clear(string $prefix = '')
{
- return $this->deleteItem($key);
+ if ('' !== $prefix) {
+ $now = microtime(true);
+
+ foreach ($this->values as $key => $value) {
+ if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || 0 === strpos($key, $prefix)) {
+ unset($this->values[$key], $this->expiries[$key]);
+ }
+ }
+
+ if ($this->values) {
+ return true;
+ }
+ }
+
+ $this->values = $this->expiries = [];
+
+ return true;
+ }
+
+ /**
+ * Returns all cached values, with cache miss as null.
+ *
+ * @return array
+ */
+ public function getValues()
+ {
+ if (!$this->storeSerialized) {
+ return $this->values;
+ }
+
+ $values = $this->values;
+ foreach ($values as $k => $v) {
+ if (null === $v || 'N;' === $v) {
+ continue;
+ }
+ if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
+ $values[$k] = serialize($v);
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reset()
+ {
+ $this->clear();
+ }
+
+ private function generateItems(array $keys, $now, $f)
+ {
+ foreach ($keys as $i => $key) {
+ if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
+ $value = null;
+
+ if (!$this->maxItems) {
+ // Track misses in non-LRU mode only
+ $this->values[$key] = null;
+ }
+ } else {
+ if ($this->maxItems) {
+ // Move the item last in the storage
+ $value = $this->values[$key];
+ unset($this->values[$key]);
+ $this->values[$key] = $value;
+ }
+
+ $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
+ }
+ unset($keys[$i]);
+
+ yield $key => $f($key, $value, $isHit);
+ }
+
+ foreach ($keys as $key) {
+ yield $key => $f($key, null, false);
+ }
+ }
+
+ private function freeze($value, $key)
+ {
+ if (null === $value) {
+ return 'N;';
+ }
+ if (\is_string($value)) {
+ // Serialize strings if they could be confused with serialized objects or arrays
+ if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
+ return serialize($value);
+ }
+ } elseif (!is_scalar($value)) {
+ try {
+ $serialized = serialize($value);
+ } catch (\Exception $e) {
+ $type = \is_object($value) ? \get_class($value) : \gettype($value);
+ $message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage());
+ CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
+
+ return;
+ }
+ // Keep value serialized if it contains any objects or any internal references
+ if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
+ return $serialized;
+ }
+ }
+
+ return $value;
+ }
+
+ private function unfreeze(string $key, bool &$isHit)
+ {
+ if ('N;' === $value = $this->values[$key]) {
+ return null;
+ }
+ if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
+ try {
+ $value = unserialize($value);
+ } catch (\Exception $e) {
+ CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
+ $value = false;
+ }
+ if (false === $value) {
+ $value = null;
+ $isHit = false;
+
+ if (!$this->maxItems) {
+ $this->values[$key] = null;
+ }
+ }
+ }
+
+ return $value;
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
index 63e97a8d0261e..a2fc8e7a3444c 100644
--- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
@@ -196,13 +196,10 @@ public function hasItem($key)
/**
* {@inheritdoc}
*
- * @param string $prefix
- *
* @return bool
*/
- public function clear(/*string $prefix = ''*/)
+ public function clear(string $prefix = '')
{
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$cleared = true;
$i = $this->adapterCount;
diff --git a/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php b/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php
new file mode 100644
index 0000000000000..b3e6f16b19fca
--- /dev/null
+++ b/src/Symfony/Component/Cache/Adapter/CouchbaseBucketAdapter.php
@@ -0,0 +1,252 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Cache\Adapter;
+
+use Symfony\Component\Cache\Exception\CacheException;
+use Symfony\Component\Cache\Exception\InvalidArgumentException;
+use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
+use Symfony\Component\Cache\Marshaller\MarshallerInterface;
+
+/**
+ * @author Antonio Jose Cerezo Aranda
+ */
+class CouchbaseBucketAdapter extends AbstractAdapter
+{
+ private const THIRTY_DAYS_IN_SECONDS = 2592000;
+ private const MAX_KEY_LENGTH = 250;
+ private const KEY_NOT_FOUND = 13;
+ private const VALID_DSN_OPTIONS = [
+ 'operationTimeout',
+ 'configTimeout',
+ 'configNodeTimeout',
+ 'n1qlTimeout',
+ 'httpTimeout',
+ 'configDelay',
+ 'htconfigIdleTimeout',
+ 'durabilityInterval',
+ 'durabilityTimeout',
+ ];
+
+ private $bucket;
+ private $marshaller;
+
+ public function __construct(\CouchbaseBucket $bucket, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
+ {
+ if (!static::isSupported()) {
+ throw new CacheException('Couchbase >= 2.6.0 is required.');
+ }
+
+ $this->maxIdLength = static::MAX_KEY_LENGTH;
+
+ $this->bucket = $bucket;
+
+ parent::__construct($namespace, $defaultLifetime);
+ $this->enableVersioning();
+ $this->marshaller = $marshaller ?? new DefaultMarshaller();
+ }
+
+ /**
+ * @param array|string $servers
+ */
+ public static function createConnection($servers, array $options = []): \CouchbaseBucket
+ {
+ if (\is_string($servers)) {
+ $servers = [$servers];
+ } elseif (!\is_array($servers)) {
+ throw new \TypeError(sprintf('Argument 1 passed to %s() must be array or string, %s given.', __METHOD__, \gettype($servers)));
+ }
+
+ if (!static::isSupported()) {
+ throw new CacheException('Couchbase >= 2.6.0 is required.');
+ }
+
+ set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
+
+ $dsnPattern = '/^(?couchbase(?:s)?)\:\/\/(?:(?[^\:]+)\:(?[^\@]{6,})@)?'
+ .'(?[^\:]+(?:\:\d+)?)(?:\/(?[^\?]+))(?:\?(?.*))?$/i';
+
+ $newServers = [];
+ $protocol = 'couchbase';
+ try {
+ $options = self::initOptions($options);
+ $username = $options['username'];
+ $password = $options['password'];
+
+ foreach ($servers as $dsn) {
+ if (0 !== strpos($dsn, 'couchbase:')) {
+ throw new InvalidArgumentException(sprintf('Invalid Couchbase DSN: %s does not start with "couchbase:".', $dsn));
+ }
+
+ preg_match($dsnPattern, $dsn, $matches);
+
+ $username = $matches['username'] ?: $username;
+ $password = $matches['password'] ?: $password;
+ $protocol = $matches['protocol'] ?: $protocol;
+
+ if (isset($matches['options'])) {
+ $optionsInDsn = self::getOptions($matches['options']);
+
+ foreach ($optionsInDsn as $parameter => $value) {
+ $options[$parameter] = $value;
+ }
+ }
+
+ $newServers[] = $matches['host'];
+ }
+
+ $connectionString = $protocol.'://'.implode(',', $newServers);
+
+ $client = new \CouchbaseCluster($connectionString);
+ $client->authenticateAs($username, $password);
+
+ $bucket = $client->openBucket($matches['bucketName']);
+
+ unset($options['username'], $options['password']);
+ foreach ($options as $option => $value) {
+ if (!empty($value)) {
+ $bucket->$option = $value;
+ }
+ }
+
+ return $bucket;
+ } finally {
+ restore_error_handler();
+ }
+ }
+
+ public static function isSupported(): bool
+ {
+ return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '2.6.0', '>=');
+ }
+
+ private static function getOptions(string $options): array
+ {
+ $results = [];
+ $optionsInArray = explode('&', $options);
+
+ foreach ($optionsInArray as $option) {
+ list($key, $value) = explode('=', $option);
+
+ if (\in_array($key, static::VALID_DSN_OPTIONS, true)) {
+ $results[$key] = $value;
+ }
+ }
+
+ return $results;
+ }
+
+ private static function initOptions(array $options): array
+ {
+ $options['username'] = $options['username'] ?? '';
+ $options['password'] = $options['password'] ?? '';
+ $options['operationTimeout'] = $options['operationTimeout'] ?? 0;
+ $options['configTimeout'] = $options['configTimeout'] ?? 0;
+ $options['configNodeTimeout'] = $options['configNodeTimeout'] ?? 0;
+ $options['n1qlTimeout'] = $options['n1qlTimeout'] ?? 0;
+ $options['httpTimeout'] = $options['httpTimeout'] ?? 0;
+ $options['configDelay'] = $options['configDelay'] ?? 0;
+ $options['htconfigIdleTimeout'] = $options['htconfigIdleTimeout'] ?? 0;
+ $options['durabilityInterval'] = $options['durabilityInterval'] ?? 0;
+ $options['durabilityTimeout'] = $options['durabilityTimeout'] ?? 0;
+
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch(array $ids)
+ {
+ $resultsCouchbase = $this->bucket->get($ids);
+
+ $results = [];
+ foreach ($resultsCouchbase as $key => $value) {
+ if (null !== $value->error) {
+ continue;
+ }
+ $results[$key] = $this->marshaller->unmarshall($value->value);
+ }
+
+ return $results;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doHave($id): bool
+ {
+ return false !== $this->bucket->get($id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doClear($namespace): bool
+ {
+ if ('' === $namespace) {
+ $this->bucket->manager()->flush();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete(array $ids): bool
+ {
+ $results = $this->bucket->remove(array_values($ids));
+
+ foreach ($results as $key => $result) {
+ if (null !== $result->error && static::KEY_NOT_FOUND !== $result->error->getCode()) {
+ continue;
+ }
+ unset($results[$key]);
+ }
+
+ return 0 === \count($results);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave(array $values, $lifetime)
+ {
+ if (!$values = $this->marshaller->marshall($values, $failed)) {
+ return $failed;
+ }
+
+ $lifetime = $this->normalizeExpiry($lifetime);
+
+ $ko = [];
+ foreach ($values as $key => $value) {
+ $result = $this->bucket->upsert($key, $value, ['expiry' => $lifetime]);
+
+ if (null !== $result->error) {
+ $ko[$key] = $result;
+ }
+ }
+
+ return [] === $ko ? true : $ko;
+ }
+
+ private function normalizeExpiry(int $expiry): int
+ {
+ if ($expiry && $expiry > static::THIRTY_DAYS_IN_SECONDS) {
+ $expiry += time();
+ }
+
+ return $expiry;
+ }
+}
diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php
index 75ae4cb7015c8..55a36e17f86c6 100644
--- a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php
@@ -12,11 +12,13 @@
namespace Symfony\Component\Cache\Adapter;
use Doctrine\Common\Cache\CacheProvider;
-use Symfony\Component\Cache\Traits\DoctrineTrait;
+/**
+ * @author Nicolas Grekas
+ */
class DoctrineAdapter extends AbstractAdapter
{
- use DoctrineTrait;
+ private $provider;
public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
{
@@ -24,4 +26,80 @@ public function __construct(CacheProvider $provider, string $namespace = '', int
$this->provider = $provider;
$provider->setNamespace($namespace);
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reset()
+ {
+ parent::reset();
+ $this->provider->setNamespace($this->provider->getNamespace());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch(array $ids)
+ {
+ $unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback');
+ try {
+ return $this->provider->fetchMultiple($ids);
+ } catch (\Error $e) {
+ $trace = $e->getTrace();
+
+ if (isset($trace[0]['function']) && !isset($trace[0]['class'])) {
+ switch ($trace[0]['function']) {
+ case 'unserialize':
+ case 'apcu_fetch':
+ case 'apc_fetch':
+ throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
+ }
+ }
+
+ throw $e;
+ } finally {
+ ini_set('unserialize_callback_func', $unserializeCallbackHandler);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doHave(string $id)
+ {
+ return $this->provider->contains($id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doClear(string $namespace)
+ {
+ $namespace = $this->provider->getNamespace();
+
+ return isset($namespace[0])
+ ? $this->provider->deleteAll()
+ : $this->provider->flushAll();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete(array $ids)
+ {
+ $ok = true;
+ foreach ($ids as $id) {
+ $ok = $this->provider->delete($id) && $ok;
+ }
+
+ return $ok;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave(array $values, int $lifetime)
+ {
+ return $this->provider->saveMultiple($values, $lifetime);
+ }
}
diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
index d9a1ad39ac6ef..2496bdab85641 100644
--- a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
@@ -44,7 +44,7 @@ public function __construct(string $namespace = '', int $defaultLifetime = 0, st
/**
* {@inheritdoc}
*/
- protected function doClear($namespace)
+ protected function doClear(string $namespace)
{
$ok = $this->doClearCache($namespace);
@@ -93,7 +93,7 @@ protected function doClear($namespace)
/**
* {@inheritdoc}
*/
- protected function doSave(array $values, ?int $lifetime, array $addTagData = [], array $removeTagData = []): array
+ protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array
{
$failed = $this->doSaveCache($values, $lifetime);
diff --git a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php
index b678bb5d8883e..e2bc068050220 100644
--- a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php
@@ -11,15 +11,30 @@
namespace Symfony\Component\Cache\Adapter;
+use Symfony\Component\Cache\Exception\CacheException;
+use Symfony\Component\Cache\Exception\InvalidArgumentException;
+use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-use Symfony\Component\Cache\Traits\MemcachedTrait;
+/**
+ * @author Rob Frawley 2nd
+ * @author Nicolas Grekas
+ */
class MemcachedAdapter extends AbstractAdapter
{
- use MemcachedTrait;
-
protected $maxIdLength = 250;
+ private static $defaultClientOptions = [
+ 'persistent_id' => null,
+ 'username' => null,
+ 'password' => null,
+ \Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP,
+ ];
+
+ private $marshaller;
+ private $client;
+ private $lazyClient;
+
/**
* Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged.
* Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that:
@@ -32,6 +47,289 @@ class MemcachedAdapter extends AbstractAdapter
*/
public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
{
- $this->init($client, $namespace, $defaultLifetime, $marshaller);
+ if (!static::isSupported()) {
+ throw new CacheException('Memcached >= 2.2.0 is required');
+ }
+ if ('Memcached' === \get_class($client)) {
+ $opt = $client->getOption(\Memcached::OPT_SERIALIZER);
+ if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
+ throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
+ }
+ $this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY));
+ $this->client = $client;
+ } else {
+ $this->lazyClient = $client;
+ }
+
+ parent::__construct($namespace, $defaultLifetime);
+ $this->enableVersioning();
+ $this->marshaller = $marshaller ?? new DefaultMarshaller();
+ }
+
+ public static function isSupported()
+ {
+ return \extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
+ }
+
+ /**
+ * Creates a Memcached instance.
+ *
+ * By default, the binary protocol, no block, and libketama compatible options are enabled.
+ *
+ * Examples for servers:
+ * - 'memcached://user:pass@localhost?weight=33'
+ * - [['localhost', 11211, 33]]
+ *
+ * @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs
+ * @param array $options An array of options
+ *
+ * @return \Memcached
+ *
+ * @throws \ErrorException When invalid options or servers are provided
+ */
+ public static function createConnection($servers, array $options = [])
+ {
+ if (\is_string($servers)) {
+ $servers = [$servers];
+ } elseif (!\is_array($servers)) {
+ throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, %s given.', \gettype($servers)));
+ }
+ if (!static::isSupported()) {
+ throw new CacheException('Memcached >= 2.2.0 is required');
+ }
+ set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
+ try {
+ $options += static::$defaultClientOptions;
+ $client = new \Memcached($options['persistent_id']);
+ $username = $options['username'];
+ $password = $options['password'];
+
+ // parse any DSN in $servers
+ foreach ($servers as $i => $dsn) {
+ if (\is_array($dsn)) {
+ continue;
+ }
+ if (0 !== strpos($dsn, 'memcached:')) {
+ throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached:"', $dsn));
+ }
+ $params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
+ if (!empty($m[2])) {
+ list($username, $password) = explode(':', $m[2], 2) + [1 => null];
+ }
+
+ return 'file:'.($m[1] ?? '');
+ }, $dsn);
+ if (false === $params = parse_url($params)) {
+ throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
+ }
+ $query = $hosts = [];
+ if (isset($params['query'])) {
+ parse_str($params['query'], $query);
+
+ if (isset($query['host'])) {
+ if (!\is_array($hosts = $query['host'])) {
+ throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
+ }
+ foreach ($hosts as $host => $weight) {
+ if (false === $port = strrpos($host, ':')) {
+ $hosts[$host] = [$host, 11211, (int) $weight];
+ } else {
+ $hosts[$host] = [substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight];
+ }
+ }
+ $hosts = array_values($hosts);
+ unset($query['host']);
+ }
+ if ($hosts && !isset($params['host']) && !isset($params['path'])) {
+ unset($servers[$i]);
+ $servers = array_merge($servers, $hosts);
+ continue;
+ }
+ }
+ if (!isset($params['host']) && !isset($params['path'])) {
+ throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
+ }
+ if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) {
+ $params['weight'] = $m[1];
+ $params['path'] = substr($params['path'], 0, -\strlen($m[0]));
+ }
+ $params += [
+ 'host' => isset($params['host']) ? $params['host'] : $params['path'],
+ 'port' => isset($params['host']) ? 11211 : null,
+ 'weight' => 0,
+ ];
+ if ($query) {
+ $params += $query;
+ $options = $query + $options;
+ }
+
+ $servers[$i] = [$params['host'], $params['port'], $params['weight']];
+
+ if ($hosts) {
+ $servers = array_merge($servers, $hosts);
+ }
+ }
+
+ // set client's options
+ unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']);
+ $options = array_change_key_case($options, CASE_UPPER);
+ $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
+ $client->setOption(\Memcached::OPT_NO_BLOCK, true);
+ $client->setOption(\Memcached::OPT_TCP_NODELAY, true);
+ if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
+ $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
+ }
+ foreach ($options as $name => $value) {
+ if (\is_int($name)) {
+ continue;
+ }
+ if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) {
+ $value = \constant('Memcached::'.$name.'_'.strtoupper($value));
+ }
+ $opt = \constant('Memcached::OPT_'.$name);
+
+ unset($options[$name]);
+ $options[$opt] = $value;
+ }
+ $client->setOptions($options);
+
+ // set client's servers, taking care of persistent connections
+ if (!$client->isPristine()) {
+ $oldServers = [];
+ foreach ($client->getServerList() as $server) {
+ $oldServers[] = [$server['host'], $server['port']];
+ }
+
+ $newServers = [];
+ foreach ($servers as $server) {
+ if (1 < \count($server)) {
+ $server = array_values($server);
+ unset($server[2]);
+ $server[1] = (int) $server[1];
+ }
+ $newServers[] = $server;
+ }
+
+ if ($oldServers !== $newServers) {
+ $client->resetServerList();
+ $client->addServers($servers);
+ }
+ } else {
+ $client->addServers($servers);
+ }
+
+ if (null !== $username || null !== $password) {
+ if (!method_exists($client, 'setSaslAuthData')) {
+ trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
+ }
+ $client->setSaslAuthData($username, $password);
+ }
+
+ return $client;
+ } finally {
+ restore_error_handler();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave(array $values, int $lifetime)
+ {
+ if (!$values = $this->marshaller->marshall($values, $failed)) {
+ return $failed;
+ }
+
+ if ($lifetime && $lifetime > 30 * 86400) {
+ $lifetime += time();
+ }
+
+ $encodedValues = [];
+ foreach ($values as $key => $value) {
+ $encodedValues[rawurlencode($key)] = $value;
+ }
+
+ return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)) ? $failed : false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch(array $ids)
+ {
+ try {
+ $encodedIds = array_map('rawurlencode', $ids);
+
+ $encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds));
+
+ $result = [];
+ foreach ($encodedResult as $key => $value) {
+ $result[rawurldecode($key)] = $this->marshaller->unmarshall($value);
+ }
+
+ return $result;
+ } catch (\Error $e) {
+ throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doHave(string $id)
+ {
+ return false !== $this->getClient()->get(rawurlencode($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete(array $ids)
+ {
+ $ok = true;
+ $encodedIds = array_map('rawurlencode', $ids);
+ foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) {
+ if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) {
+ $ok = false;
+ }
+ }
+
+ return $ok;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doClear(string $namespace)
+ {
+ return '' === $namespace && $this->getClient()->flush();
+ }
+
+ private function checkResultCode($result)
+ {
+ $code = $this->client->getResultCode();
+
+ if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) {
+ return $result;
+ }
+
+ throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage())));
+ }
+
+ private function getClient(): \Memcached
+ {
+ if ($this->client) {
+ return $this->client;
+ }
+
+ $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER);
+ if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
+ throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
+ }
+ if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) {
+ throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix));
+ }
+
+ return $this->client = $this->lazyClient;
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/NullAdapter.php b/src/Symfony/Component/Cache/Adapter/NullAdapter.php
index a2fdd36373b0c..44778d787b083 100644
--- a/src/Symfony/Component/Cache/Adapter/NullAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/NullAdapter.php
@@ -78,11 +78,9 @@ public function hasItem($key)
/**
* {@inheritdoc}
*
- * @param string $prefix
- *
* @return bool
*/
- public function clear(/*string $prefix = ''*/)
+ public function clear(string $prefix = '')
{
return true;
}
diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
index d118736aec067..99a671516af6f 100644
--- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
@@ -12,17 +12,35 @@
namespace Symfony\Component\Cache\Adapter;
use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
+use Doctrine\DBAL\DriverManager;
+use Doctrine\DBAL\Exception\TableNotFoundException;
+use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
+use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\Traits\PdoTrait;
class PdoAdapter extends AbstractAdapter implements PruneableInterface
{
- use PdoTrait;
-
protected $maxIdLength = 255;
+ private $marshaller;
+ private $conn;
+ private $dsn;
+ private $driver;
+ private $serverVersion;
+ private $table = 'cache_items';
+ private $idCol = 'item_id';
+ private $dataCol = 'item_data';
+ private $lifetimeCol = 'item_lifetime';
+ private $timeCol = 'item_time';
+ private $username = '';
+ private $password = '';
+ private $connectionOptions = [];
+ private $namespace;
+
/**
* You can either pass an existing database connection as PDO instance or
* a Doctrine DBAL Connection or a DSN string that will be used to
@@ -49,6 +67,404 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
*/
public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null)
{
- $this->init($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
+ if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
+ throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
+ }
+
+ if ($connOrDsn instanceof \PDO) {
+ if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
+ throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
+ }
+
+ $this->conn = $connOrDsn;
+ } elseif ($connOrDsn instanceof Connection) {
+ $this->conn = $connOrDsn;
+ } elseif (\is_string($connOrDsn)) {
+ $this->dsn = $connOrDsn;
+ } else {
+ throw new InvalidArgumentException(sprintf('"%s" requires PDO or Doctrine\DBAL\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, \is_object($connOrDsn) ? \get_class($connOrDsn) : \gettype($connOrDsn)));
+ }
+
+ $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table;
+ $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol;
+ $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol;
+ $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol;
+ $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol;
+ $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username;
+ $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password;
+ $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions;
+ $this->namespace = $namespace;
+ $this->marshaller = $marshaller ?? new DefaultMarshaller();
+
+ parent::__construct($namespace, $defaultLifetime);
+ }
+
+ /**
+ * Creates the table to store cache items which can be called once for setup.
+ *
+ * Cache ID are saved in a column of maximum length 255. Cache data is
+ * saved in a BLOB.
+ *
+ * @throws \PDOException When the table already exists
+ * @throws DBALException When the table already exists
+ * @throws \DomainException When an unsupported PDO driver is used
+ */
+ public function createTable()
+ {
+ // connect if we are not yet
+ $conn = $this->getConnection();
+
+ if ($conn instanceof Connection) {
+ $types = [
+ 'mysql' => 'binary',
+ 'sqlite' => 'text',
+ 'pgsql' => 'string',
+ 'oci' => 'string',
+ 'sqlsrv' => 'string',
+ ];
+ if (!isset($types[$this->driver])) {
+ throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
+ }
+
+ $schema = new Schema();
+ $table = $schema->createTable($this->table);
+ $table->addColumn($this->idCol, $types[$this->driver], ['length' => 255]);
+ $table->addColumn($this->dataCol, 'blob', ['length' => 16777215]);
+ $table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]);
+ $table->addColumn($this->timeCol, 'integer', ['unsigned' => true]);
+ $table->setPrimaryKey([$this->idCol]);
+
+ foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
+ $conn->exec($sql);
+ }
+
+ return;
+ }
+
+ switch ($this->driver) {
+ case 'mysql':
+ // We use varbinary for the ID column because it prevents unwanted conversions:
+ // - character set conversions between server and client
+ // - trailing space removal
+ // - case-insensitivity
+ // - language processing like é == e
+ $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(255) NOT NULL PRIMARY KEY, $this->dataCol MEDIUMBLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
+ break;
+ case 'sqlite':
+ $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'pgsql':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'oci':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(255) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'sqlsrv':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
+ break;
+ default:
+ throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
+ }
+
+ $conn->exec($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prune()
+ {
+ $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time";
+
+ if ('' !== $this->namespace) {
+ $deleteSql .= " AND $this->idCol LIKE :namespace";
+ }
+
+ try {
+ $delete = $this->getConnection()->prepare($deleteSql);
+ } catch (TableNotFoundException $e) {
+ return true;
+ } catch (\PDOException $e) {
+ return true;
+ }
+ $delete->bindValue(':time', time(), \PDO::PARAM_INT);
+
+ if ('' !== $this->namespace) {
+ $delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR);
+ }
+ try {
+ return $delete->execute();
+ } catch (TableNotFoundException $e) {
+ return true;
+ } catch (\PDOException $e) {
+ return true;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch(array $ids)
+ {
+ $now = time();
+ $expired = [];
+
+ $sql = str_pad('', (\count($ids) << 1) - 1, '?,');
+ $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN ($sql)";
+ $stmt = $this->getConnection()->prepare($sql);
+ $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
+ foreach ($ids as $id) {
+ $stmt->bindValue(++$i, $id);
+ }
+ $stmt->execute();
+
+ while ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
+ if (null === $row[1]) {
+ $expired[] = $row[0];
+ } else {
+ yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]);
+ }
+ }
+
+ if ($expired) {
+ $sql = str_pad('', (\count($expired) << 1) - 1, '?,');
+ $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN ($sql)";
+ $stmt = $this->getConnection()->prepare($sql);
+ $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
+ foreach ($expired as $id) {
+ $stmt->bindValue(++$i, $id);
+ }
+ $stmt->execute();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doHave(string $id)
+ {
+ $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = :id AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > :time)";
+ $stmt = $this->getConnection()->prepare($sql);
+
+ $stmt->bindValue(':id', $id);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ $stmt->execute();
+
+ return (bool) $stmt->fetchColumn();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doClear(string $namespace)
+ {
+ $conn = $this->getConnection();
+
+ if ('' === $namespace) {
+ if ('sqlite' === $this->driver) {
+ $sql = "DELETE FROM $this->table";
+ } else {
+ $sql = "TRUNCATE TABLE $this->table";
+ }
+ } else {
+ $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
+ }
+
+ try {
+ $conn->exec($sql);
+ } catch (TableNotFoundException $e) {
+ } catch (\PDOException $e) {
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete(array $ids)
+ {
+ $sql = str_pad('', (\count($ids) << 1) - 1, '?,');
+ $sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)";
+ try {
+ $stmt = $this->getConnection()->prepare($sql);
+ $stmt->execute(array_values($ids));
+ } catch (TableNotFoundException $e) {
+ } catch (\PDOException $e) {
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave(array $values, int $lifetime)
+ {
+ if (!$values = $this->marshaller->marshall($values, $failed)) {
+ return $failed;
+ }
+
+ $conn = $this->getConnection();
+ $driver = $this->driver;
+ $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
+
+ switch (true) {
+ case 'mysql' === $driver:
+ $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
+ break;
+ case 'oci' === $driver:
+ // DUAL is Oracle specific dummy table
+ $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ".
+ "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
+ "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?";
+ break;
+ case 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '10', '>='):
+ // MERGE is only available since SQL Server 2008 and must be terminated by semicolon
+ // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
+ $sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
+ "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
+ "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
+ break;
+ case 'sqlite' === $driver:
+ $sql = 'INSERT OR REPLACE'.substr($insertSql, 6);
+ break;
+ case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='):
+ $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
+ break;
+ default:
+ $driver = null;
+ $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
+ break;
+ }
+
+ $now = time();
+ $lifetime = $lifetime ?: null;
+ try {
+ $stmt = $conn->prepare($sql);
+ } catch (TableNotFoundException $e) {
+ if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
+ $this->createTable();
+ }
+ $stmt = $conn->prepare($sql);
+ } catch (\PDOException $e) {
+ if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
+ $this->createTable();
+ }
+ $stmt = $conn->prepare($sql);
+ }
+
+ if ('sqlsrv' === $driver || 'oci' === $driver) {
+ $stmt->bindParam(1, $id);
+ $stmt->bindParam(2, $id);
+ $stmt->bindParam(3, $data, \PDO::PARAM_LOB);
+ $stmt->bindValue(4, $lifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(5, $now, \PDO::PARAM_INT);
+ $stmt->bindParam(6, $data, \PDO::PARAM_LOB);
+ $stmt->bindValue(7, $lifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(8, $now, \PDO::PARAM_INT);
+ } else {
+ $stmt->bindParam(':id', $id);
+ $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(':time', $now, \PDO::PARAM_INT);
+ }
+ if (null === $driver) {
+ $insertStmt = $conn->prepare($insertSql);
+
+ $insertStmt->bindParam(':id', $id);
+ $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
+ $insertStmt->bindValue(':time', $now, \PDO::PARAM_INT);
+ }
+
+ foreach ($values as $id => $data) {
+ try {
+ $stmt->execute();
+ } catch (TableNotFoundException $e) {
+ if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
+ $this->createTable();
+ }
+ $stmt->execute();
+ } catch (\PDOException $e) {
+ if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
+ $this->createTable();
+ }
+ $stmt->execute();
+ }
+ if (null === $driver && !$stmt->rowCount()) {
+ try {
+ $insertStmt->execute();
+ } catch (DBALException $e) {
+ } catch (\PDOException $e) {
+ // A concurrent write won, let it be
+ }
+ }
+ }
+
+ return $failed;
+ }
+
+ /**
+ * @return \PDO|Connection
+ */
+ private function getConnection(): object
+ {
+ if (null === $this->conn) {
+ if (strpos($this->dsn, '://')) {
+ if (!class_exists(DriverManager::class)) {
+ throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $this->dsn));
+ }
+ $this->conn = DriverManager::getConnection(['url' => $this->dsn]);
+ } else {
+ $this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions);
+ $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ }
+ }
+ if (null === $this->driver) {
+ if ($this->conn instanceof \PDO) {
+ $this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME);
+ } else {
+ switch ($this->driver = $this->conn->getDriver()->getName()) {
+ case 'mysqli':
+ throw new \LogicException(sprintf('The adapter "%s" does not support the mysqli driver, use pdo_mysql instead.', static::class));
+ case 'pdo_mysql':
+ case 'drizzle_pdo_mysql':
+ $this->driver = 'mysql';
+ break;
+ case 'pdo_sqlite':
+ $this->driver = 'sqlite';
+ break;
+ case 'pdo_pgsql':
+ $this->driver = 'pgsql';
+ break;
+ case 'oci8':
+ case 'pdo_oracle':
+ $this->driver = 'oci';
+ break;
+ case 'pdo_sqlsrv':
+ $this->driver = 'sqlsrv';
+ break;
+ }
+ }
+ }
+
+ return $this->conn;
+ }
+
+ private function getServerVersion(): string
+ {
+ if (null === $this->serverVersion) {
+ $conn = $this->conn instanceof \PDO ? $this->conn : $this->conn->getWrappedConnection();
+ if ($conn instanceof \PDO) {
+ $this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
+ } elseif ($conn instanceof ServerInfoAwareConnection) {
+ $this->serverVersion = $conn->getServerVersion();
+ } else {
+ $this->serverVersion = '0';
+ }
+ }
+
+ return $this->serverVersion;
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php
index b4f13d1346e31..84946f6446d0a 100644
--- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php
@@ -18,7 +18,8 @@
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ContractsTrait;
-use Symfony\Component\Cache\Traits\PhpArrayTrait;
+use Symfony\Component\Cache\Traits\ProxyTrait;
+use Symfony\Component\VarExporter\VarExporter;
use Symfony\Contracts\Cache\CacheInterface;
/**
@@ -30,11 +31,16 @@
*/
class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
{
- use PhpArrayTrait;
use ContractsTrait;
+ use ProxyTrait;
+ private $file;
+ private $keys;
+ private $values;
private $createCacheItem;
+ private static $valuesCache = [];
+
/**
* @param string $file The PHP file were values are cached
* @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit
@@ -65,7 +71,7 @@ static function ($key, $value, $isHit) {
*
* @return CacheItemPoolInterface
*/
- public static function create($file, CacheItemPoolInterface $fallbackPool)
+ public static function create(string $file, CacheItemPoolInterface $fallbackPool)
{
if (!$fallbackPool instanceof AdapterInterface) {
$fallbackPool = new ProxyAdapter($fallbackPool);
@@ -262,6 +268,138 @@ public function commit()
return $this->pool->commit();
}
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function clear(string $prefix = '')
+ {
+ $this->keys = $this->values = [];
+
+ $cleared = @unlink($this->file) || !file_exists($this->file);
+ unset(self::$valuesCache[$this->file]);
+
+ if ($this->pool instanceof AdapterInterface) {
+ return $this->pool->clear($prefix) && $cleared;
+ }
+
+ return $this->pool->clear() && $cleared;
+ }
+
+ /**
+ * Store an array of cached values.
+ *
+ * @param array $values The cached values
+ */
+ public function warmUp(array $values)
+ {
+ if (file_exists($this->file)) {
+ if (!is_file($this->file)) {
+ throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s.', $this->file));
+ }
+
+ if (!is_writable($this->file)) {
+ throw new InvalidArgumentException(sprintf('Cache file is not writable: %s.', $this->file));
+ }
+ } else {
+ $directory = \dirname($this->file);
+
+ if (!is_dir($directory) && !@mkdir($directory, 0777, true)) {
+ throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s.', $directory));
+ }
+
+ if (!is_writable($directory)) {
+ throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s.', $directory));
+ }
+ }
+
+ $dumpedValues = '';
+ $dumpedMap = [];
+ $dump = <<<'EOF'
+ $value) {
+ CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
+ $isStaticValue = true;
+
+ if (null === $value) {
+ $value = "'N;'";
+ } elseif (\is_object($value) || \is_array($value)) {
+ try {
+ $value = VarExporter::export($value, $isStaticValue);
+ } catch (\Exception $e) {
+ throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
+ }
+ } elseif (\is_string($value)) {
+ // Wrap "N;" in a closure to not confuse it with an encoded `null`
+ if ('N;' === $value) {
+ $isStaticValue = false;
+ }
+ $value = var_export($value, true);
+ } elseif (!is_scalar($value)) {
+ throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
+ } else {
+ $value = var_export($value, true);
+ }
+
+ if (!$isStaticValue) {
+ $value = str_replace("\n", "\n ", $value);
+ $value = "static function () {\n return {$value};\n}";
+ }
+ $hash = hash('md5', $value);
+
+ if (null === $id = $dumpedMap[$hash] ?? null) {
+ $id = $dumpedMap[$hash] = \count($dumpedMap);
+ $dumpedValues .= "{$id} => {$value},\n";
+ }
+
+ $dump .= var_export($key, true)." => {$id},\n";
+ }
+
+ $dump .= "\n], [\n\n{$dumpedValues}\n]];\n";
+
+ $tmpFile = uniqid($this->file, true);
+
+ file_put_contents($tmpFile, $dump);
+ @chmod($tmpFile, 0666 & ~umask());
+ unset($serialized, $value, $dump);
+
+ @rename($tmpFile, $this->file);
+ unset(self::$valuesCache[$this->file]);
+
+ $this->initialize();
+ }
+
+ /**
+ * Load the cache file.
+ */
+ private function initialize()
+ {
+ if (isset(self::$valuesCache[$this->file])) {
+ $values = self::$valuesCache[$this->file];
+ } elseif (!file_exists($this->file)) {
+ $this->keys = $this->values = [];
+
+ return;
+ } else {
+ $values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []];
+ }
+
+ if (2 !== \count($values) || !isset($values[0], $values[1])) {
+ $this->keys = $this->values = [];
+ } else {
+ list($this->keys, $this->values) = $values;
+ }
+ }
+
private function generateItems(array $keys): \Generator
{
$f = $this->createCacheItem;
@@ -291,42 +429,4 @@ private function generateItems(array $keys): \Generator
yield from $this->pool->getItems($fallbackKeys);
}
}
-
- /**
- * @throws \ReflectionException When $class is not found and is required
- *
- * @internal to be removed in Symfony 5.0
- */
- public static function throwOnRequiredClass($class)
- {
- $e = new \ReflectionException("Class $class does not exist");
- $trace = debug_backtrace();
- $autoloadFrame = [
- 'function' => 'spl_autoload_call',
- 'args' => [$class],
- ];
- $i = 1 + array_search($autoloadFrame, $trace, true);
-
- if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) {
- switch ($trace[$i]['function']) {
- case 'get_class_methods':
- case 'get_class_vars':
- case 'get_parent_class':
- case 'is_a':
- case 'is_subclass_of':
- case 'class_exists':
- case 'class_implements':
- case 'class_parents':
- case 'trait_exists':
- case 'defined':
- case 'interface_exists':
- case 'method_exists':
- case 'property_exists':
- case 'is_callable':
- return;
- }
- }
-
- throw $e;
- }
}
diff --git a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
index 10938a0a9e921..f148a8909fe23 100644
--- a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
@@ -12,12 +12,30 @@
namespace Symfony\Component\Cache\Adapter;
use Symfony\Component\Cache\Exception\CacheException;
+use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\Traits\PhpFilesTrait;
+use Symfony\Component\Cache\Traits\FilesystemCommonTrait;
+use Symfony\Component\VarExporter\VarExporter;
+/**
+ * @author Piotr Stankowski
+ * @author Nicolas Grekas
+ * @author Rob Frawley 2nd
+ */
class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
{
- use PhpFilesTrait;
+ use FilesystemCommonTrait {
+ doClear as private doCommonClear;
+ doDelete as private doCommonDelete;
+ }
+
+ private $includeHandler;
+ private $appendOnly;
+ private $values = [];
+ private $files = [];
+
+ private static $startTime;
+ private static $valuesCache = [];
/**
* @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
@@ -35,4 +53,278 @@ public function __construct(string $namespace = '', int $defaultLifetime = 0, st
throw new \ErrorException($msg, 0, $type, $file, $line);
};
}
+
+ public static function isSupported()
+ {
+ self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
+
+ return \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN));
+ }
+
+ /**
+ * @return bool
+ */
+ public function prune()
+ {
+ $time = time();
+ $pruned = true;
+ $getExpiry = true;
+
+ set_error_handler($this->includeHandler);
+ try {
+ foreach ($this->scanHashDir($this->directory) as $file) {
+ try {
+ if (\is_array($expiresAt = include $file)) {
+ $expiresAt = $expiresAt[0];
+ }
+ } catch (\ErrorException $e) {
+ $expiresAt = $time;
+ }
+
+ if ($time >= $expiresAt) {
+ $pruned = $this->doUnlink($file) && !file_exists($file) && $pruned;
+ }
+ }
+ } finally {
+ restore_error_handler();
+ }
+
+ return $pruned;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch(array $ids)
+ {
+ if ($this->appendOnly) {
+ $now = 0;
+ $missingIds = [];
+ } else {
+ $now = time();
+ $missingIds = $ids;
+ $ids = [];
+ }
+ $values = [];
+
+ begin:
+ $getExpiry = false;
+
+ foreach ($ids as $id) {
+ if (null === $value = $this->values[$id] ?? null) {
+ $missingIds[] = $id;
+ } elseif ('N;' === $value) {
+ $values[$id] = null;
+ } elseif (!\is_object($value)) {
+ $values[$id] = $value;
+ } elseif (!$value instanceof LazyValue) {
+ $values[$id] = $value();
+ } elseif (false === $values[$id] = include $value->file) {
+ unset($values[$id], $this->values[$id]);
+ $missingIds[] = $id;
+ }
+ if (!$this->appendOnly) {
+ unset($this->values[$id]);
+ }
+ }
+
+ if (!$missingIds) {
+ return $values;
+ }
+
+ set_error_handler($this->includeHandler);
+ try {
+ $getExpiry = true;
+
+ foreach ($missingIds as $k => $id) {
+ try {
+ $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
+
+ if (isset(self::$valuesCache[$file])) {
+ [$expiresAt, $this->values[$id]] = self::$valuesCache[$file];
+ } elseif (\is_array($expiresAt = include $file)) {
+ if ($this->appendOnly) {
+ self::$valuesCache[$file] = $expiresAt;
+ }
+
+ [$expiresAt, $this->values[$id]] = $expiresAt;
+ } elseif ($now < $expiresAt) {
+ $this->values[$id] = new LazyValue($file);
+ }
+
+ if ($now >= $expiresAt) {
+ unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
+ }
+ } catch (\ErrorException $e) {
+ unset($missingIds[$k]);
+ }
+ }
+ } finally {
+ restore_error_handler();
+ }
+
+ $ids = $missingIds;
+ $missingIds = [];
+ goto begin;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doHave(string $id)
+ {
+ if ($this->appendOnly && isset($this->values[$id])) {
+ return true;
+ }
+
+ set_error_handler($this->includeHandler);
+ try {
+ $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
+ $getExpiry = true;
+
+ if (isset(self::$valuesCache[$file])) {
+ [$expiresAt, $value] = self::$valuesCache[$file];
+ } elseif (\is_array($expiresAt = include $file)) {
+ if ($this->appendOnly) {
+ self::$valuesCache[$file] = $expiresAt;
+ }
+
+ [$expiresAt, $value] = $expiresAt;
+ } elseif ($this->appendOnly) {
+ $value = new LazyValue($file);
+ }
+ } catch (\ErrorException $e) {
+ return false;
+ } finally {
+ restore_error_handler();
+ }
+ if ($this->appendOnly) {
+ $now = 0;
+ $this->values[$id] = $value;
+ } else {
+ $now = time();
+ }
+
+ return $now < $expiresAt;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave(array $values, int $lifetime)
+ {
+ $ok = true;
+ $expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX';
+ $allowCompile = self::isSupported();
+
+ foreach ($values as $key => $value) {
+ unset($this->values[$key]);
+ $isStaticValue = true;
+ if (null === $value) {
+ $value = "'N;'";
+ } elseif (\is_object($value) || \is_array($value)) {
+ try {
+ $value = VarExporter::export($value, $isStaticValue);
+ } catch (\Exception $e) {
+ throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
+ }
+ } elseif (\is_string($value)) {
+ // Wrap "N;" in a closure to not confuse it with an encoded `null`
+ if ('N;' === $value) {
+ $isStaticValue = false;
+ }
+ $value = var_export($value, true);
+ } elseif (!is_scalar($value)) {
+ throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
+ } else {
+ $value = var_export($value, true);
+ }
+
+ $encodedKey = rawurlencode($key);
+
+ if ($isStaticValue) {
+ $value = "return [{$expiry}, {$value}];";
+ } elseif ($this->appendOnly) {
+ $value = "return [{$expiry}, static function () { return {$value}; }];";
+ } else {
+ // We cannot use a closure here because of https://bugs.php.net/76982
+ $value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
+ $value = "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
+ }
+
+ $file = $this->files[$key] = $this->getFile($key, true);
+ // Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
+ $ok = $this->write($file, "directory)) {
+ throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory));
+ }
+
+ return $ok;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doClear(string $namespace)
+ {
+ $this->values = [];
+
+ return $this->doCommonClear($namespace);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete(array $ids)
+ {
+ foreach ($ids as $id) {
+ unset($this->values[$id]);
+ }
+
+ return $this->doCommonDelete($ids);
+ }
+
+ protected function doUnlink($file)
+ {
+ unset(self::$valuesCache[$file]);
+
+ if (self::isSupported()) {
+ @opcache_invalidate($file, true);
+ }
+
+ return @unlink($file);
+ }
+
+ private function getFileKey(string $file): string
+ {
+ if (!$h = @fopen($file, 'rb')) {
+ return '';
+ }
+
+ $encodedKey = substr(fgets($h), 8);
+ fclose($h);
+
+ return rawurldecode(rtrim($encodedKey));
+ }
+}
+
+/**
+ * @internal
+ */
+class LazyValue
+{
+ public $file;
+
+ public function __construct($file)
+ {
+ $this->file = $file;
+ }
}
diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
index bccafb31cd549..bd96fa87b9f2f 100644
--- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
@@ -150,14 +150,10 @@ public function hasItem($key)
/**
* {@inheritdoc}
*
- * @param string $prefix
- *
* @return bool
*/
- public function clear(/*string $prefix = ''*/)
+ public function clear(string $prefix = '')
{
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
-
if ($this->pool instanceof AdapterInterface) {
return $this->pool->clear($this->namespace.$prefix);
}
diff --git a/src/Symfony/Component/Cache/Adapter/Psr16Adapter.php b/src/Symfony/Component/Cache/Adapter/Psr16Adapter.php
index bb38871019d25..a13b20ebc21c6 100644
--- a/src/Symfony/Component/Cache/Adapter/Psr16Adapter.php
+++ b/src/Symfony/Component/Cache/Adapter/Psr16Adapter.php
@@ -55,7 +55,7 @@ protected function doFetch(array $ids)
/**
* {@inheritdoc}
*/
- protected function doHave($id)
+ protected function doHave(string $id)
{
return $this->pool->has($id);
}
@@ -63,7 +63,7 @@ protected function doHave($id)
/**
* {@inheritdoc}
*/
- protected function doClear($namespace)
+ protected function doClear(string $namespace)
{
return $this->pool->clear();
}
@@ -79,7 +79,7 @@ protected function doDelete(array $ids)
/**
* {@inheritdoc}
*/
- protected function doSave(array $values, $lifetime)
+ protected function doSave(array $values, int $lifetime)
{
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
}
diff --git a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php
index f936afd589e31..f382f223f2635 100644
--- a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php
@@ -91,7 +91,7 @@ public function __construct($redisClient, string $namespace = '', int $defaultLi
/**
* {@inheritdoc}
*/
- protected function doSave(array $values, ?int $lifetime, array $addTagData = [], array $delTagData = []): array
+ protected function doSave(array $values, int $lifetime, array $addTagData = [], array $delTagData = []): array
{
$eviction = $this->getRedisEvictionPolicy();
if ('noeviction' !== $eviction && 0 !== strpos($eviction, 'volatile-')) {
diff --git a/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php b/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php
deleted file mode 100644
index d0d42e57f0a7c..0000000000000
--- a/src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php
+++ /dev/null
@@ -1,21 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Adapter;
-
-@trigger_error(sprintf('The "%s" class is @deprecated since Symfony 4.3, use "Psr16Adapter" instead.', SimpleCacheAdapter::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use Psr16Adapter instead.
- */
-class SimpleCacheAdapter extends Psr16Adapter
-{
-}
diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
index 7c0ee30024370..793df433f9717 100644
--- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
@@ -225,14 +225,10 @@ public function getItems(array $keys = [])
/**
* {@inheritdoc}
*
- * @param string $prefix
- *
* @return bool
*/
- public function clear(/*string $prefix = ''*/)
+ public function clear(string $prefix = '')
{
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
-
if ('' !== $prefix) {
foreach ($this->deferred as $key => $item) {
if (0 === strpos($key, $prefix)) {
diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
index 7e9dac803c32d..16d819ee46d4b 100644
--- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
@@ -176,13 +176,10 @@ public function getItems(array $keys = [])
/**
* {@inheritdoc}
*
- * @param string $prefix
- *
* @return bool
*/
- public function clear(/*string $prefix = ''*/)
+ public function clear(string $prefix = '')
{
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$event = $this->start(__FUNCTION__);
try {
if ($this->pool instanceof AdapterInterface) {
diff --git a/src/Symfony/Component/Cache/CHANGELOG.md b/src/Symfony/Component/Cache/CHANGELOG.md
index 435eaf3d9b67e..75b35defb88ba 100644
--- a/src/Symfony/Component/Cache/CHANGELOG.md
+++ b/src/Symfony/Component/Cache/CHANGELOG.md
@@ -1,6 +1,20 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * added max-items + LRU + max-lifetime capabilities to `ArrayCache`
+ * added `CouchbaseBucketAdapter`
+
+5.0.0
+-----
+
+ * removed all PSR-16 implementations in the `Simple` namespace
+ * removed `SimpleCacheAdapter`
+ * removed `AbstractAdapter::unserialize()`
+ * removed `CacheItem::getPreviousTags()`
+
4.4.0
-----
@@ -12,6 +26,7 @@ CHANGELOG
* removed support for phpredis 4 `compression`
* [BC BREAK] `RedisTagAwareAdapter` is not compatible with `RedisCluster` from `Predis` anymore, use `phpredis` instead
* Marked the `CacheDataCollector` class as `@final`.
+ * added `SodiumMarshaller` to encrypt/decrypt values using libsodium
4.3.0
-----
diff --git a/src/Symfony/Component/Cache/CacheItem.php b/src/Symfony/Component/Cache/CacheItem.php
index 3abd50e0a411b..5880803db9d6a 100644
--- a/src/Symfony/Component/Cache/CacheItem.php
+++ b/src/Symfony/Component/Cache/CacheItem.php
@@ -146,18 +146,6 @@ public function getMetadata(): array
return $this->metadata;
}
- /**
- * Returns the list of tags bound to the value coming from the pool storage if any.
- *
- * @deprecated since Symfony 4.2, use the "getMetadata()" method instead.
- */
- public function getPreviousTags(): array
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "getMetadata()" method instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->metadata[self::METADATA_TAGS] ?? [];
- }
-
/**
* Validates a cache key according to PSR-6.
*
diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php
index ed86fcadfc8b7..a96dc3ab852ff 100644
--- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php
+++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php
@@ -22,7 +22,7 @@
* @author Aaron Scherer
* @author Tobias Nyholm
*
- * @final since Symfony 4.4
+ * @final
*/
class CacheDataCollector extends DataCollector implements LateDataCollectorInterface
{
@@ -31,20 +31,15 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
*/
private $instances = [];
- /**
- * @param string $name
- */
- public function addInstance($name, TraceableAdapter $instance)
+ public function addInstance(string $name, TraceableAdapter $instance)
{
$this->instances[$name] = $instance;
}
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
$empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []];
$this->data = ['instances' => $empty, 'total' => $empty];
diff --git a/src/Symfony/Component/Cache/LockRegistry.php b/src/Symfony/Component/Cache/LockRegistry.php
index 80601be70e48e..ac2670c231373 100644
--- a/src/Symfony/Component/Cache/LockRegistry.php
+++ b/src/Symfony/Component/Cache/LockRegistry.php
@@ -39,6 +39,7 @@ final class LockRegistry
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ApcuAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ArrayAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ChainAdapter.php',
+ __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'CouchbaseBucketAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'DoctrineAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemTagAwareAdapter.php',
@@ -51,7 +52,6 @@ final class LockRegistry
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'Psr16Adapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'RedisAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'RedisTagAwareAdapter.php',
- __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'SimpleCacheAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapterInterface.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TraceableAdapter.php',
diff --git a/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php b/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php
index f4cad03fe18f1..62f1a9082b1a3 100644
--- a/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php
+++ b/src/Symfony/Component/Cache/Marshaller/DefaultMarshaller.php
@@ -92,7 +92,7 @@ public function unmarshall(string $value)
/**
* @internal
*/
- public static function handleUnserializeCallback($class)
+ public static function handleUnserializeCallback(string $class)
{
throw new \DomainException('Class not found: '.$class);
}
diff --git a/src/Symfony/Component/Cache/Marshaller/SodiumMarshaller.php b/src/Symfony/Component/Cache/Marshaller/SodiumMarshaller.php
new file mode 100644
index 0000000000000..dbf486a721e47
--- /dev/null
+++ b/src/Symfony/Component/Cache/Marshaller/SodiumMarshaller.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Cache\Marshaller;
+
+use Symfony\Component\Cache\Exception\CacheException;
+use Symfony\Component\Cache\Exception\InvalidArgumentException;
+
+/**
+ * Encrypt/decrypt values using Libsodium.
+ *
+ * @author Ahmed TAILOULOUTE
+ */
+class SodiumMarshaller implements MarshallerInterface
+{
+ private $marshaller;
+ private $decryptionKeys;
+
+ /**
+ * @param string[] $decryptionKeys The key at index "0" is required and is used to decrypt and encrypt values;
+ * more rotating keys can be provided to decrypt values;
+ * each key must be generated using sodium_crypto_box_keypair()
+ */
+ public function __construct(array $decryptionKeys, MarshallerInterface $marshaller = null)
+ {
+ if (!self::isSupported()) {
+ throw new CacheException('The "sodium" PHP extension is not loaded.');
+ }
+
+ if (!isset($decryptionKeys[0])) {
+ throw new InvalidArgumentException('At least one decryption key must be provided at index "0".');
+ }
+
+ $this->marshaller = $marshaller ?? new DefaultMarshaller();
+ $this->decryptionKeys = $decryptionKeys;
+ }
+
+ public static function isSupported(): bool
+ {
+ return \function_exists('sodium_crypto_box_seal');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function marshall(array $values, ?array &$failed): array
+ {
+ $encryptionKey = sodium_crypto_box_publickey($this->decryptionKeys[0]);
+
+ $encryptedValues = [];
+ foreach ($this->marshaller->marshall($values, $failed) as $k => $v) {
+ $encryptedValues[$k] = sodium_crypto_box_seal($v, $encryptionKey);
+ }
+
+ return $encryptedValues;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unmarshall(string $value)
+ {
+ foreach ($this->decryptionKeys as $k) {
+ if (false !== $decryptedValue = @sodium_crypto_box_seal_open($value, $k)) {
+ $value = $decryptedValue;
+ break;
+ }
+ }
+
+ return $this->marshaller->unmarshall($value);
+ }
+}
diff --git a/src/Symfony/Component/Cache/Simple/AbstractCache.php b/src/Symfony/Component/Cache/Simple/AbstractCache.php
deleted file mode 100644
index b3477e94f36f0..0000000000000
--- a/src/Symfony/Component/Cache/Simple/AbstractCache.php
+++ /dev/null
@@ -1,199 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Psr\Log\LoggerAwareInterface;
-use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
-use Symfony\Component\Cache\Adapter\AbstractAdapter;
-use Symfony\Component\Cache\CacheItem;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\Cache\ResettableInterface;
-use Symfony\Component\Cache\Traits\AbstractTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', AbstractCache::class, AbstractAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use AbstractAdapter and type-hint for CacheInterface instead.
- */
-abstract class AbstractCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
-{
- /**
- * @internal
- */
- protected const NS_SEPARATOR = ':';
-
- use AbstractTrait {
- deleteItems as private;
- AbstractTrait::deleteItem as delete;
- AbstractTrait::hasItem as has;
- }
-
- private $defaultLifetime;
-
- protected function __construct(string $namespace = '', int $defaultLifetime = 0)
- {
- $this->defaultLifetime = max(0, $defaultLifetime);
- $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
- if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
- throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace));
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function get($key, $default = null)
- {
- $id = $this->getId($key);
-
- try {
- foreach ($this->doFetch([$id]) as $value) {
- return $value;
- }
- } catch (\Exception $e) {
- CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
- }
-
- return $default;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function set($key, $value, $ttl = null)
- {
- CacheItem::validateKey($key);
-
- return $this->setMultiple([$key => $value], $ttl);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return iterable
- */
- public function getMultiple($keys, $default = null)
- {
- if ($keys instanceof \Traversable) {
- $keys = iterator_to_array($keys, false);
- } elseif (!\is_array($keys)) {
- throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
- }
- $ids = [];
-
- foreach ($keys as $key) {
- $ids[] = $this->getId($key);
- }
- try {
- $values = $this->doFetch($ids);
- } catch (\Exception $e) {
- CacheItem::log($this->logger, 'Failed to fetch values: '.$e->getMessage(), ['keys' => $keys, 'exception' => $e]);
- $values = [];
- }
- $ids = array_combine($ids, $keys);
-
- return $this->generateValues($values, $ids, $default);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function setMultiple($values, $ttl = null)
- {
- if (!\is_array($values) && !$values instanceof \Traversable) {
- throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values)));
- }
- $valuesById = [];
-
- foreach ($values as $key => $value) {
- if (\is_int($key)) {
- $key = (string) $key;
- }
- $valuesById[$this->getId($key)] = $value;
- }
- if (false === $ttl = $this->normalizeTtl($ttl)) {
- return $this->doDelete(array_keys($valuesById));
- }
-
- try {
- $e = $this->doSave($valuesById, $ttl);
- } catch (\Exception $e) {
- }
- if (true === $e || [] === $e) {
- return true;
- }
- $keys = [];
- foreach (\is_array($e) ? $e : array_keys($valuesById) as $id) {
- $keys[] = substr($id, \strlen($this->namespace));
- }
- $message = 'Failed to save values'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
- CacheItem::log($this->logger, $message, ['keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null]);
-
- return false;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteMultiple($keys)
- {
- if ($keys instanceof \Traversable) {
- $keys = iterator_to_array($keys, false);
- } elseif (!\is_array($keys)) {
- throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
- }
-
- return $this->deleteItems($keys);
- }
-
- private function normalizeTtl($ttl)
- {
- if (null === $ttl) {
- return $this->defaultLifetime;
- }
- if ($ttl instanceof \DateInterval) {
- $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U');
- }
- if (\is_int($ttl)) {
- return 0 < $ttl ? $ttl : false;
- }
-
- throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl)));
- }
-
- private function generateValues(iterable $values, array &$keys, $default): iterable
- {
- try {
- foreach ($values as $id => $value) {
- if (!isset($keys[$id])) {
- $id = key($keys);
- }
- $key = $keys[$id];
- unset($keys[$id]);
- yield $key => $value;
- }
- } catch (\Exception $e) {
- CacheItem::log($this->logger, 'Failed to fetch values: '.$e->getMessage(), ['keys' => array_values($keys), 'exception' => $e]);
- }
-
- foreach ($keys as $key) {
- yield $key => $default;
- }
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/ApcuCache.php b/src/Symfony/Component/Cache/Simple/ApcuCache.php
deleted file mode 100644
index f1eb946ff5c39..0000000000000
--- a/src/Symfony/Component/Cache/Simple/ApcuCache.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Adapter\ApcuAdapter;
-use Symfony\Component\Cache\Traits\ApcuTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ApcuCache::class, ApcuAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use ApcuAdapter and type-hint for CacheInterface instead.
- */
-class ApcuCache extends AbstractCache
-{
- use ApcuTrait;
-
- public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null)
- {
- $this->init($namespace, $defaultLifetime, $version);
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/ArrayCache.php b/src/Symfony/Component/Cache/Simple/ArrayCache.php
deleted file mode 100644
index 7174825707bb7..0000000000000
--- a/src/Symfony/Component/Cache/Simple/ArrayCache.php
+++ /dev/null
@@ -1,167 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Psr\Log\LoggerAwareInterface;
-use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
-use Symfony\Component\Cache\Adapter\ArrayAdapter;
-use Symfony\Component\Cache\CacheItem;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\Cache\ResettableInterface;
-use Symfony\Component\Cache\Traits\ArrayTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ArrayCache::class, ArrayAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use ArrayAdapter and type-hint for CacheInterface instead.
- */
-class ArrayCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
-{
- use ArrayTrait {
- ArrayTrait::deleteItem as delete;
- ArrayTrait::hasItem as has;
- }
-
- private $defaultLifetime;
-
- /**
- * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
- */
- public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
- {
- $this->defaultLifetime = $defaultLifetime;
- $this->storeSerialized = $storeSerialized;
- }
-
- /**
- * {@inheritdoc}
- */
- public function get($key, $default = null)
- {
- if (!\is_string($key) || !isset($this->expiries[$key])) {
- CacheItem::validateKey($key);
- }
- if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > microtime(true) || !$this->delete($key))) {
- $this->values[$key] = null;
-
- return $default;
- }
- if (!$this->storeSerialized) {
- return $this->values[$key];
- }
- $value = $this->unfreeze($key, $isHit);
-
- return $isHit ? $value : $default;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return iterable
- */
- public function getMultiple($keys, $default = null)
- {
- if ($keys instanceof \Traversable) {
- $keys = iterator_to_array($keys, false);
- } elseif (!\is_array($keys)) {
- throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
- }
- foreach ($keys as $key) {
- if (!\is_string($key) || !isset($this->expiries[$key])) {
- CacheItem::validateKey($key);
- }
- }
-
- return $this->generateItems($keys, microtime(true), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; });
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteMultiple($keys)
- {
- if (!\is_array($keys) && !$keys instanceof \Traversable) {
- throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
- }
- foreach ($keys as $key) {
- $this->delete($key);
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function set($key, $value, $ttl = null)
- {
- if (!\is_string($key)) {
- CacheItem::validateKey($key);
- }
-
- return $this->setMultiple([$key => $value], $ttl);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function setMultiple($values, $ttl = null)
- {
- if (!\is_array($values) && !$values instanceof \Traversable) {
- throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values)));
- }
- $valuesArray = [];
-
- foreach ($values as $key => $value) {
- if (!\is_int($key) && !(\is_string($key) && isset($this->expiries[$key]))) {
- CacheItem::validateKey($key);
- }
- $valuesArray[$key] = $value;
- }
- if (false === $ttl = $this->normalizeTtl($ttl)) {
- return $this->deleteMultiple(array_keys($valuesArray));
- }
- $expiry = 0 < $ttl ? microtime(true) + $ttl : PHP_INT_MAX;
-
- foreach ($valuesArray as $key => $value) {
- if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
- return false;
- }
- $this->values[$key] = $value;
- $this->expiries[$key] = $expiry;
- }
-
- return true;
- }
-
- private function normalizeTtl($ttl)
- {
- if (null === $ttl) {
- return $this->defaultLifetime;
- }
- if ($ttl instanceof \DateInterval) {
- $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U');
- }
- if (\is_int($ttl)) {
- return 0 < $ttl ? $ttl : false;
- }
-
- throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl)));
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/ChainCache.php b/src/Symfony/Component/Cache/Simple/ChainCache.php
deleted file mode 100644
index 57a169f63227d..0000000000000
--- a/src/Symfony/Component/Cache/Simple/ChainCache.php
+++ /dev/null
@@ -1,271 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
-use Symfony\Component\Cache\Adapter\ChainAdapter;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\ResettableInterface;
-use Symfony\Contracts\Cache\CacheInterface;
-use Symfony\Contracts\Service\ResetInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ChainCache::class, ChainAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * Chains several caches together.
- *
- * Cached items are fetched from the first cache having them in its data store.
- * They are saved and deleted in all caches at once.
- *
- * @deprecated since Symfony 4.3, use ChainAdapter and type-hint for CacheInterface instead.
- */
-class ChainCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
-{
- private $miss;
- private $caches = [];
- private $defaultLifetime;
- private $cacheCount;
-
- /**
- * @param Psr16CacheInterface[] $caches The ordered list of caches used to fetch cached items
- * @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones
- */
- public function __construct(array $caches, int $defaultLifetime = 0)
- {
- if (!$caches) {
- throw new InvalidArgumentException('At least one cache must be specified.');
- }
-
- foreach ($caches as $cache) {
- if (!$cache instanceof Psr16CacheInterface) {
- throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), Psr16CacheInterface::class));
- }
- }
-
- $this->miss = new \stdClass();
- $this->caches = array_values($caches);
- $this->cacheCount = \count($this->caches);
- $this->defaultLifetime = 0 < $defaultLifetime ? $defaultLifetime : null;
- }
-
- /**
- * {@inheritdoc}
- */
- public function get($key, $default = null)
- {
- $miss = null !== $default && \is_object($default) ? $default : $this->miss;
-
- foreach ($this->caches as $i => $cache) {
- $value = $cache->get($key, $miss);
-
- if ($miss !== $value) {
- while (0 <= --$i) {
- $this->caches[$i]->set($key, $value, $this->defaultLifetime);
- }
-
- return $value;
- }
- }
-
- return $default;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return iterable
- */
- public function getMultiple($keys, $default = null)
- {
- $miss = null !== $default && \is_object($default) ? $default : $this->miss;
-
- return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default);
- }
-
- private function generateItems(iterable $values, int $cacheIndex, $miss, $default): iterable
- {
- $missing = [];
- $nextCacheIndex = $cacheIndex + 1;
- $nextCache = isset($this->caches[$nextCacheIndex]) ? $this->caches[$nextCacheIndex] : null;
-
- foreach ($values as $k => $value) {
- if ($miss !== $value) {
- yield $k => $value;
- } elseif (!$nextCache) {
- yield $k => $default;
- } else {
- $missing[] = $k;
- }
- }
-
- if ($missing) {
- $cache = $this->caches[$cacheIndex];
- $values = $this->generateItems($nextCache->getMultiple($missing, $miss), $nextCacheIndex, $miss, $default);
-
- foreach ($values as $k => $value) {
- if ($miss !== $value) {
- $cache->set($k, $value, $this->defaultLifetime);
- yield $k => $value;
- } else {
- yield $k => $default;
- }
- }
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function has($key)
- {
- foreach ($this->caches as $cache) {
- if ($cache->has($key)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function clear()
- {
- $cleared = true;
- $i = $this->cacheCount;
-
- while ($i--) {
- $cleared = $this->caches[$i]->clear() && $cleared;
- }
-
- return $cleared;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function delete($key)
- {
- $deleted = true;
- $i = $this->cacheCount;
-
- while ($i--) {
- $deleted = $this->caches[$i]->delete($key) && $deleted;
- }
-
- return $deleted;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteMultiple($keys)
- {
- if ($keys instanceof \Traversable) {
- $keys = iterator_to_array($keys, false);
- }
- $deleted = true;
- $i = $this->cacheCount;
-
- while ($i--) {
- $deleted = $this->caches[$i]->deleteMultiple($keys) && $deleted;
- }
-
- return $deleted;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function set($key, $value, $ttl = null)
- {
- $saved = true;
- $i = $this->cacheCount;
-
- while ($i--) {
- $saved = $this->caches[$i]->set($key, $value, $ttl) && $saved;
- }
-
- return $saved;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function setMultiple($values, $ttl = null)
- {
- if ($values instanceof \Traversable) {
- $valuesIterator = $values;
- $values = function () use ($valuesIterator, &$values) {
- $generatedValues = [];
-
- foreach ($valuesIterator as $key => $value) {
- yield $key => $value;
- $generatedValues[$key] = $value;
- }
-
- $values = $generatedValues;
- };
- $values = $values();
- }
- $saved = true;
- $i = $this->cacheCount;
-
- while ($i--) {
- $saved = $this->caches[$i]->setMultiple($values, $ttl) && $saved;
- }
-
- return $saved;
- }
-
- /**
- * {@inheritdoc}
- */
- public function prune()
- {
- $pruned = true;
-
- foreach ($this->caches as $cache) {
- if ($cache instanceof PruneableInterface) {
- $pruned = $cache->prune() && $pruned;
- }
- }
-
- return $pruned;
- }
-
- /**
- * {@inheritdoc}
- */
- public function reset()
- {
- foreach ($this->caches as $cache) {
- if ($cache instanceof ResetInterface) {
- $cache->reset();
- }
- }
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/DoctrineCache.php b/src/Symfony/Component/Cache/Simple/DoctrineCache.php
deleted file mode 100644
index 6a6d0031533e8..0000000000000
--- a/src/Symfony/Component/Cache/Simple/DoctrineCache.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Doctrine\Common\Cache\CacheProvider;
-use Symfony\Component\Cache\Adapter\DoctrineAdapter;
-use Symfony\Component\Cache\Traits\DoctrineTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', DoctrineCache::class, DoctrineAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use DoctrineAdapter and type-hint for CacheInterface instead.
- */
-class DoctrineCache extends AbstractCache
-{
- use DoctrineTrait;
-
- public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
- {
- parent::__construct('', $defaultLifetime);
- $this->provider = $provider;
- $provider->setNamespace($namespace);
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/FilesystemCache.php b/src/Symfony/Component/Cache/Simple/FilesystemCache.php
deleted file mode 100644
index 8891abd94c9fe..0000000000000
--- a/src/Symfony/Component/Cache/Simple/FilesystemCache.php
+++ /dev/null
@@ -1,36 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Adapter\FilesystemAdapter;
-use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
-use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\Traits\FilesystemTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', FilesystemCache::class, FilesystemAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use FilesystemAdapter and type-hint for CacheInterface instead.
- */
-class FilesystemCache extends AbstractCache implements PruneableInterface
-{
- use FilesystemTrait;
-
- public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
- {
- $this->marshaller = $marshaller ?? new DefaultMarshaller();
- parent::__construct('', $defaultLifetime);
- $this->init($namespace, $directory);
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/MemcachedCache.php b/src/Symfony/Component/Cache/Simple/MemcachedCache.php
deleted file mode 100644
index e1934119aa74a..0000000000000
--- a/src/Symfony/Component/Cache/Simple/MemcachedCache.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Adapter\MemcachedAdapter;
-use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-use Symfony\Component\Cache\Traits\MemcachedTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', MemcachedCache::class, MemcachedAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use MemcachedAdapter and type-hint for CacheInterface instead.
- */
-class MemcachedCache extends AbstractCache
-{
- use MemcachedTrait;
-
- protected $maxIdLength = 250;
-
- public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
- {
- $this->init($client, $namespace, $defaultLifetime, $marshaller);
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/NullCache.php b/src/Symfony/Component/Cache/Simple/NullCache.php
deleted file mode 100644
index 35600fc6da123..0000000000000
--- a/src/Symfony/Component/Cache/Simple/NullCache.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
-use Symfony\Component\Cache\Adapter\NullAdapter;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', NullCache::class, NullAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use NullAdapter and type-hint for CacheInterface instead.
- */
-class NullCache implements Psr16CacheInterface
-{
- /**
- * {@inheritdoc}
- */
- public function get($key, $default = null)
- {
- return $default;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return iterable
- */
- public function getMultiple($keys, $default = null)
- {
- foreach ($keys as $key) {
- yield $key => $default;
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function has($key)
- {
- return false;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function clear()
- {
- return true;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function delete($key)
- {
- return true;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteMultiple($keys)
- {
- return true;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function set($key, $value, $ttl = null)
- {
- return false;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function setMultiple($values, $ttl = null)
- {
- return false;
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/PdoCache.php b/src/Symfony/Component/Cache/Simple/PdoCache.php
deleted file mode 100644
index 07849e93feb9a..0000000000000
--- a/src/Symfony/Component/Cache/Simple/PdoCache.php
+++ /dev/null
@@ -1,59 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Adapter\PdoAdapter;
-use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\Traits\PdoTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PdoCache::class, PdoAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use PdoAdapter and type-hint for CacheInterface instead.
- */
-class PdoCache extends AbstractCache implements PruneableInterface
-{
- use PdoTrait;
-
- protected $maxIdLength = 255;
-
- /**
- * You can either pass an existing database connection as PDO instance or
- * a Doctrine DBAL Connection or a DSN string that will be used to
- * lazy-connect to the database when the cache is actually used.
- *
- * When a Doctrine DBAL Connection is passed, the cache table is created
- * automatically when possible. Otherwise, use the createTable() method.
- *
- * List of available options:
- * * db_table: The name of the table [default: cache_items]
- * * db_id_col: The column where to store the cache id [default: item_id]
- * * db_data_col: The column where to store the cache data [default: item_data]
- * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime]
- * * db_time_col: The column where to store the timestamp [default: item_time]
- * * db_username: The username when lazy-connect [default: '']
- * * db_password: The password when lazy-connect [default: '']
- * * db_connection_options: An array of driver-specific connection options [default: []]
- *
- * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null
- *
- * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string
- * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
- * @throws InvalidArgumentException When namespace contains invalid characters
- */
- public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null)
- {
- $this->init($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/PhpArrayCache.php b/src/Symfony/Component/Cache/Simple/PhpArrayCache.php
deleted file mode 100644
index 3711f462904c3..0000000000000
--- a/src/Symfony/Component/Cache/Simple/PhpArrayCache.php
+++ /dev/null
@@ -1,256 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
-use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\ResettableInterface;
-use Symfony\Component\Cache\Traits\PhpArrayTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PhpArrayCache::class, PhpArrayAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use PhpArrayAdapter and type-hint for CacheInterface instead.
- */
-class PhpArrayCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
-{
- use PhpArrayTrait;
-
- /**
- * @param string $file The PHP file were values are cached
- * @param Psr16CacheInterface $fallbackPool A pool to fallback on when an item is not hit
- */
- public function __construct(string $file, Psr16CacheInterface $fallbackPool)
- {
- $this->file = $file;
- $this->pool = $fallbackPool;
- }
-
- /**
- * This adapter takes advantage of how PHP stores arrays in its latest versions.
- *
- * @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
- */
- public static function create($file, Psr16CacheInterface $fallbackPool)
- {
- return new static($file, $fallbackPool);
- }
-
- /**
- * {@inheritdoc}
- */
- public function get($key, $default = null)
- {
- if (!\is_string($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
- if (null === $this->values) {
- $this->initialize();
- }
- if (!isset($this->keys[$key])) {
- return $this->pool->get($key, $default);
- }
- $value = $this->values[$this->keys[$key]];
-
- if ('N;' === $value) {
- return null;
- }
- if ($value instanceof \Closure) {
- try {
- return $value();
- } catch (\Throwable $e) {
- return $default;
- }
- }
-
- return $value;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return iterable
- */
- public function getMultiple($keys, $default = null)
- {
- if ($keys instanceof \Traversable) {
- $keys = iterator_to_array($keys, false);
- } elseif (!\is_array($keys)) {
- throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
- }
- foreach ($keys as $key) {
- if (!\is_string($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
- }
- if (null === $this->values) {
- $this->initialize();
- }
-
- return $this->generateItems($keys, $default);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function has($key)
- {
- if (!\is_string($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
- if (null === $this->values) {
- $this->initialize();
- }
-
- return isset($this->keys[$key]) || $this->pool->has($key);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function delete($key)
- {
- if (!\is_string($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
- if (null === $this->values) {
- $this->initialize();
- }
-
- return !isset($this->keys[$key]) && $this->pool->delete($key);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteMultiple($keys)
- {
- if (!\is_array($keys) && !$keys instanceof \Traversable) {
- throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
- }
-
- $deleted = true;
- $fallbackKeys = [];
-
- foreach ($keys as $key) {
- if (!\is_string($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
-
- if (isset($this->keys[$key])) {
- $deleted = false;
- } else {
- $fallbackKeys[] = $key;
- }
- }
- if (null === $this->values) {
- $this->initialize();
- }
-
- if ($fallbackKeys) {
- $deleted = $this->pool->deleteMultiple($fallbackKeys) && $deleted;
- }
-
- return $deleted;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function set($key, $value, $ttl = null)
- {
- if (!\is_string($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
- if (null === $this->values) {
- $this->initialize();
- }
-
- return !isset($this->keys[$key]) && $this->pool->set($key, $value, $ttl);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function setMultiple($values, $ttl = null)
- {
- if (!\is_array($values) && !$values instanceof \Traversable) {
- throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values)));
- }
-
- $saved = true;
- $fallbackValues = [];
-
- foreach ($values as $key => $value) {
- if (!\is_string($key) && !\is_int($key)) {
- throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
- }
-
- if (isset($this->keys[$key])) {
- $saved = false;
- } else {
- $fallbackValues[$key] = $value;
- }
- }
-
- if ($fallbackValues) {
- $saved = $this->pool->setMultiple($fallbackValues, $ttl) && $saved;
- }
-
- return $saved;
- }
-
- private function generateItems(array $keys, $default): iterable
- {
- $fallbackKeys = [];
-
- foreach ($keys as $key) {
- if (isset($this->keys[$key])) {
- $value = $this->values[$this->keys[$key]];
-
- if ('N;' === $value) {
- yield $key => null;
- } elseif ($value instanceof \Closure) {
- try {
- yield $key => $value();
- } catch (\Throwable $e) {
- yield $key => $default;
- }
- } else {
- yield $key => $value;
- }
- } else {
- $fallbackKeys[] = $key;
- }
- }
-
- if ($fallbackKeys) {
- yield from $this->pool->getMultiple($fallbackKeys, $default);
- }
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/PhpFilesCache.php b/src/Symfony/Component/Cache/Simple/PhpFilesCache.php
deleted file mode 100644
index 060244a086643..0000000000000
--- a/src/Symfony/Component/Cache/Simple/PhpFilesCache.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
-use Symfony\Component\Cache\Exception\CacheException;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\Traits\PhpFilesTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PhpFilesCache::class, PhpFilesAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use PhpFilesAdapter and type-hint for CacheInterface instead.
- */
-class PhpFilesCache extends AbstractCache implements PruneableInterface
-{
- use PhpFilesTrait;
-
- /**
- * @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
- * Doing so is encouraged because it fits perfectly OPcache's memory model.
- *
- * @throws CacheException if OPcache is not enabled
- */
- public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = false)
- {
- $this->appendOnly = $appendOnly;
- self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
- parent::__construct('', $defaultLifetime);
- $this->init($namespace, $directory);
- $this->includeHandler = static function ($type, $msg, $file, $line) {
- throw new \ErrorException($msg, 0, $type, $file, $line);
- };
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/Psr6Cache.php b/src/Symfony/Component/Cache/Simple/Psr6Cache.php
deleted file mode 100644
index 090f48c172860..0000000000000
--- a/src/Symfony/Component/Cache/Simple/Psr6Cache.php
+++ /dev/null
@@ -1,23 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Psr16Cache;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', Psr6Cache::class, Psr16Cache::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use Psr16Cache instead.
- */
-class Psr6Cache extends Psr16Cache
-{
-}
diff --git a/src/Symfony/Component/Cache/Simple/RedisCache.php b/src/Symfony/Component/Cache/Simple/RedisCache.php
deleted file mode 100644
index 9655a753e1072..0000000000000
--- a/src/Symfony/Component/Cache/Simple/RedisCache.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Symfony\Component\Cache\Adapter\RedisAdapter;
-use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-use Symfony\Component\Cache\Traits\RedisTrait;
-use Symfony\Contracts\Cache\CacheInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', RedisCache::class, RedisAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use RedisAdapter and type-hint for CacheInterface instead.
- */
-class RedisCache extends AbstractCache
-{
- use RedisTrait;
-
- /**
- * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface $redisClient
- */
- public function __construct($redisClient, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
- {
- $this->init($redisClient, $namespace, $defaultLifetime, $marshaller);
- }
-}
diff --git a/src/Symfony/Component/Cache/Simple/TraceableCache.php b/src/Symfony/Component/Cache/Simple/TraceableCache.php
deleted file mode 100644
index 2214f1c818975..0000000000000
--- a/src/Symfony/Component/Cache/Simple/TraceableCache.php
+++ /dev/null
@@ -1,258 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Simple;
-
-use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
-use Symfony\Component\Cache\Adapter\TraceableAdapter;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\ResettableInterface;
-use Symfony\Contracts\Cache\CacheInterface;
-use Symfony\Contracts\Service\ResetInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', TraceableCache::class, TraceableAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.3, use TraceableAdapter and type-hint for CacheInterface instead.
- */
-class TraceableCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
-{
- private $pool;
- private $miss;
- private $calls = [];
-
- public function __construct(Psr16CacheInterface $pool)
- {
- $this->pool = $pool;
- $this->miss = new \stdClass();
- }
-
- /**
- * {@inheritdoc}
- */
- public function get($key, $default = null)
- {
- $miss = null !== $default && \is_object($default) ? $default : $this->miss;
- $event = $this->start(__FUNCTION__);
- try {
- $value = $this->pool->get($key, $miss);
- } finally {
- $event->end = microtime(true);
- }
- if ($event->result[$key] = $miss !== $value) {
- ++$event->hits;
- } else {
- ++$event->misses;
- $value = $default;
- }
-
- return $value;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function has($key)
- {
- $event = $this->start(__FUNCTION__);
- try {
- return $event->result[$key] = $this->pool->has($key);
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function delete($key)
- {
- $event = $this->start(__FUNCTION__);
- try {
- return $event->result[$key] = $this->pool->delete($key);
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function set($key, $value, $ttl = null)
- {
- $event = $this->start(__FUNCTION__);
- try {
- return $event->result[$key] = $this->pool->set($key, $value, $ttl);
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function setMultiple($values, $ttl = null)
- {
- $event = $this->start(__FUNCTION__);
- $event->result['keys'] = [];
-
- if ($values instanceof \Traversable) {
- $values = function () use ($values, $event) {
- foreach ($values as $k => $v) {
- $event->result['keys'][] = $k;
- yield $k => $v;
- }
- };
- $values = $values();
- } elseif (\is_array($values)) {
- $event->result['keys'] = array_keys($values);
- }
-
- try {
- return $event->result['result'] = $this->pool->setMultiple($values, $ttl);
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return iterable
- */
- public function getMultiple($keys, $default = null)
- {
- $miss = null !== $default && \is_object($default) ? $default : $this->miss;
- $event = $this->start(__FUNCTION__);
- try {
- $result = $this->pool->getMultiple($keys, $miss);
- } finally {
- $event->end = microtime(true);
- }
- $f = function () use ($result, $event, $miss, $default) {
- $event->result = [];
- foreach ($result as $key => $value) {
- if ($event->result[$key] = $miss !== $value) {
- ++$event->hits;
- } else {
- ++$event->misses;
- $value = $default;
- }
- yield $key => $value;
- }
- };
-
- return $f();
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function clear()
- {
- $event = $this->start(__FUNCTION__);
- try {
- return $event->result = $this->pool->clear();
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteMultiple($keys)
- {
- $event = $this->start(__FUNCTION__);
- if ($keys instanceof \Traversable) {
- $keys = $event->result['keys'] = iterator_to_array($keys, false);
- } else {
- $event->result['keys'] = $keys;
- }
- try {
- return $event->result['result'] = $this->pool->deleteMultiple($keys);
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function prune()
- {
- if (!$this->pool instanceof PruneableInterface) {
- return false;
- }
- $event = $this->start(__FUNCTION__);
- try {
- return $event->result = $this->pool->prune();
- } finally {
- $event->end = microtime(true);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function reset()
- {
- if (!$this->pool instanceof ResetInterface) {
- return;
- }
- $event = $this->start(__FUNCTION__);
- try {
- $this->pool->reset();
- } finally {
- $event->end = microtime(true);
- }
- }
-
- public function getCalls()
- {
- try {
- return $this->calls;
- } finally {
- $this->calls = [];
- }
- }
-
- private function start(string $name): TraceableCacheEvent
- {
- $this->calls[] = $event = new TraceableCacheEvent();
- $event->name = $name;
- $event->start = microtime(true);
-
- return $event;
- }
-}
-
-class TraceableCacheEvent
-{
- public $name;
- public $start;
- public $end;
- public $result;
- public $hits = 0;
- public $misses = 0;
-}
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php
index ff37479cc17da..f23ca6b806643 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php
@@ -55,4 +55,36 @@ public function testGetValuesHitAndMiss()
$this->assertArrayHasKey('bar', $values);
$this->assertNull($values['bar']);
}
+
+ public function testMaxLifetime()
+ {
+ $cache = new ArrayAdapter(0, false, 1);
+
+ $item = $cache->getItem('foo');
+ $item->expiresAfter(2);
+ $cache->save($item->set(123));
+
+ $this->assertTrue($cache->hasItem('foo'));
+ sleep(1);
+ $this->assertFalse($cache->hasItem('foo'));
+ }
+
+ public function testMaxItems()
+ {
+ $cache = new ArrayAdapter(0, false, 0, 2);
+
+ $cache->save($cache->getItem('foo'));
+ $cache->save($cache->getItem('bar'));
+ $cache->save($cache->getItem('buz'));
+
+ $this->assertFalse($cache->hasItem('foo'));
+ $this->assertTrue($cache->hasItem('bar'));
+ $this->assertTrue($cache->hasItem('buz'));
+
+ $cache->save($cache->getItem('foo'));
+
+ $this->assertFalse($cache->hasItem('bar'));
+ $this->assertTrue($cache->hasItem('buz'));
+ $this->assertTrue($cache->hasItem('foo'));
+ }
}
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php
new file mode 100644
index 0000000000000..d93c74fc52984
--- /dev/null
+++ b/src/Symfony/Component/Cache/Tests/Adapter/CouchbaseBucketAdapterTest.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Cache\Tests\Adapter;
+
+use Psr\Cache\CacheItemPoolInterface;
+use Symfony\Component\Cache\Adapter\AbstractAdapter;
+use Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter;
+
+/**
+ * @requires extension couchbase 2.6.0
+ *
+ * @author Antonio Jose Cerezo Aranda
+ */
+class CouchbaseBucketAdapterTest extends AdapterTestCase
+{
+ protected $skippedTests = [
+ 'testClearPrefix' => 'Couchbase cannot clear by prefix',
+ ];
+
+ /** @var \CouchbaseBucket */
+ protected static $client;
+
+ public static function setupBeforeClass(): void
+ {
+ self::$client = AbstractAdapter::createConnection('couchbase://'.getenv('COUCHBASE_HOST').'/cache',
+ ['username' => getenv('COUCHBASE_USER'), 'password' => getenv('COUCHBASE_PASS')]
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
+ {
+ $client = $defaultLifetime
+ ? AbstractAdapter::createConnection('couchbase://'
+ .getenv('COUCHBASE_USER')
+ .':'.getenv('COUCHBASE_PASS')
+ .'@'.getenv('COUCHBASE_HOST')
+ .'/cache')
+ : self::$client;
+
+ return new CouchbaseBucketAdapter($client, str_replace('\\', '.', __CLASS__), $defaultLifetime);
+ }
+}
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/SimpleCacheAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/SimpleCacheAdapterTest.php
deleted file mode 100644
index b4e185fcc9c24..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Adapter/SimpleCacheAdapterTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Adapter;
-
-use Psr\Cache\CacheItemPoolInterface;
-use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
-use Symfony\Component\Cache\Simple\ArrayCache;
-use Symfony\Component\Cache\Simple\FilesystemCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class SimpleCacheAdapterTest extends AdapterTestCase
-{
- protected $skippedTests = [
- 'testPrune' => 'SimpleCache just proxies',
- 'testClearPrefix' => 'SimpleCache cannot clear by prefix',
- ];
-
- public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
- {
- return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
- }
-
- public function testValidCacheKeyWithNamespace()
- {
- $cache = new SimpleCacheAdapter(new ArrayCache(), 'some_namespace', 0);
- $item = $cache->getItem('my_key');
- $item->set('someValue');
- $cache->save($item);
-
- $this->assertTrue($cache->getItem('my_key')->isHit(), 'Stored item is successfully retrieved.');
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/DependencyInjection/CachePoolClearerPassTest.php b/src/Symfony/Component/Cache/Tests/DependencyInjection/CachePoolClearerPassTest.php
index 533aa14afff06..202e05c4ab863 100644
--- a/src/Symfony/Component/Cache/Tests/DependencyInjection/CachePoolClearerPassTest.php
+++ b/src/Symfony/Component/Cache/Tests/DependencyInjection/CachePoolClearerPassTest.php
@@ -15,7 +15,6 @@
use Symfony\Component\Cache\DependencyInjection\CachePoolClearerPass;
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass;
-use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
@@ -53,12 +52,6 @@ public function testPoolRefsAreWeak()
$container->setAlias('clearer_alias', 'clearer');
$pass = new RemoveUnusedDefinitionsPass();
- foreach ($container->getCompiler()->getPassConfig()->getRemovingPasses() as $removingPass) {
- if ($removingPass instanceof RepeatedPass) {
- $pass->setRepeatedPass(new RepeatedPass([$pass]));
- break;
- }
- }
foreach ([new CachePoolPass(), $pass, new CachePoolClearerPass()] as $pass) {
$pass->process($container);
}
diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/SodiumMarshallerTest.php b/src/Symfony/Component/Cache/Tests/Marshaller/SodiumMarshallerTest.php
new file mode 100644
index 0000000000000..bd80fb10dc9d9
--- /dev/null
+++ b/src/Symfony/Component/Cache/Tests/Marshaller/SodiumMarshallerTest.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Cache\Tests\Marshaller;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
+use Symfony\Component\Cache\Marshaller\SodiumMarshaller;
+
+/**
+ * @requires extension sodium
+ */
+class SodiumMarshallerTest extends TestCase
+{
+ private $decryptionKey;
+
+ protected function setUp(): void
+ {
+ $this->decryptionKey = sodium_crypto_box_keypair();
+ }
+
+ public function testMarshall()
+ {
+ $defaultMarshaller = new DefaultMarshaller();
+ $sodiumMarshaller = new SodiumMarshaller([$this->decryptionKey], $defaultMarshaller);
+
+ $values = ['a' => '123'];
+ $failed = [];
+ $defaultResult = $defaultMarshaller->marshall($values, $failed);
+
+ $sodiumResult = $sodiumMarshaller->marshall($values, $failed);
+ $sodiumResult['a'] = sodium_crypto_box_seal_open($sodiumResult['a'], $this->decryptionKey);
+
+ $this->assertSame($defaultResult, $sodiumResult);
+ }
+
+ public function testUnmarshall()
+ {
+ $defaultMarshaller = new DefaultMarshaller();
+ $sodiumMarshaller = new SodiumMarshaller([$this->decryptionKey], $defaultMarshaller);
+
+ $values = ['a' => '123'];
+ $failed = [];
+
+ $sodiumResult = $sodiumMarshaller->marshall($values, $failed);
+ $defaultResult = $defaultMarshaller->marshall($values, $failed);
+
+ $this->assertSame($values['a'], $sodiumMarshaller->unmarshall($sodiumResult['a']));
+ $this->assertSame($values['a'], $sodiumMarshaller->unmarshall($defaultResult['a']));
+
+ $sodiumMarshaller = new SodiumMarshaller([sodium_crypto_box_keypair(), $this->decryptionKey], $defaultMarshaller);
+
+ $this->assertSame($values['a'], $sodiumMarshaller->unmarshall($sodiumResult['a']));
+ $this->assertSame($values['a'], $sodiumMarshaller->unmarshall($defaultResult['a']));
+ }
+}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/AbstractRedisCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/AbstractRedisCacheTest.php
deleted file mode 100644
index 4023c43105c14..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/AbstractRedisCacheTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\RedisCache;
-
-/**
- * @group legacy
- */
-abstract class AbstractRedisCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testSetTtl' => 'Testing expiration slows down the test suite',
- 'testSetMultipleTtl' => 'Testing expiration slows down the test suite',
- 'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
- ];
-
- protected static $redis;
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new RedisCache(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
- }
-
- public static function setUpBeforeClass(): void
- {
- if (!\extension_loaded('redis')) {
- self::markTestSkipped('Extension redis required.');
- }
- if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
- $e = error_get_last();
- self::markTestSkipped($e['message']);
- }
- }
-
- public static function tearDownAfterClass(): void
- {
- self::$redis = null;
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/ApcuCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/ApcuCacheTest.php
deleted file mode 100644
index 0f5c87e226db7..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/ApcuCacheTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\ApcuCache;
-
-/**
- * @group legacy
- */
-class ApcuCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testSetTtl' => 'Testing expiration slows down the test suite',
- 'testSetMultipleTtl' => 'Testing expiration slows down the test suite',
- 'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
- ];
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- if (!\function_exists('apcu_fetch') || !filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) || ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN))) {
- $this->markTestSkipped('APCu extension is required.');
- }
- if ('\\' === \DIRECTORY_SEPARATOR) {
- $this->markTestSkipped('Fails transiently on Windows.');
- }
-
- return new ApcuCache(str_replace('\\', '.', __CLASS__), $defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/ArrayCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/ArrayCacheTest.php
deleted file mode 100644
index 8d21f4f4914d4..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/ArrayCacheTest.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\ArrayCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class ArrayCacheTest extends CacheTestCase
-{
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new ArrayCache($defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
deleted file mode 100644
index 5d7e30f27afb4..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
+++ /dev/null
@@ -1,141 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Cache\IntegrationTests\SimpleCacheTest;
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\PruneableInterface;
-
-abstract class CacheTestCase extends SimpleCacheTest
-{
- protected function setUp(): void
- {
- parent::setUp();
-
- if (!\array_key_exists('testPrune', $this->skippedTests) && !$this->createSimpleCache() instanceof PruneableInterface) {
- $this->skippedTests['testPrune'] = 'Not a pruneable cache pool.';
- }
- }
-
- public static function validKeys(): array
- {
- return array_merge(parent::validKeys(), [["a\0b"]]);
- }
-
- public function testDefaultLifeTime()
- {
- if (isset($this->skippedTests[__FUNCTION__])) {
- $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
- }
-
- $cache = $this->createSimpleCache(2);
- $cache->clear();
-
- $cache->set('key.dlt', 'value');
- sleep(1);
-
- $this->assertSame('value', $cache->get('key.dlt'));
-
- sleep(2);
- $this->assertNull($cache->get('key.dlt'));
-
- $cache->clear();
- }
-
- public function testNotUnserializable()
- {
- if (isset($this->skippedTests[__FUNCTION__])) {
- $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
- }
-
- $cache = $this->createSimpleCache();
- $cache->clear();
-
- $cache->set('foo', new NotUnserializable());
-
- $this->assertNull($cache->get('foo'));
-
- $cache->setMultiple(['foo' => new NotUnserializable()]);
-
- foreach ($cache->getMultiple(['foo']) as $value) {
- }
- $this->assertNull($value);
-
- $cache->clear();
- }
-
- public function testPrune()
- {
- if (isset($this->skippedTests[__FUNCTION__])) {
- $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
- }
-
- if (!method_exists($this, 'isPruned')) {
- $this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.');
- }
-
- /** @var PruneableInterface|CacheInterface $cache */
- $cache = $this->createSimpleCache();
- $cache->clear();
-
- $cache->set('foo', 'foo-val', new \DateInterval('PT05S'));
- $cache->set('bar', 'bar-val', new \DateInterval('PT10S'));
- $cache->set('baz', 'baz-val', new \DateInterval('PT15S'));
- $cache->set('qux', 'qux-val', new \DateInterval('PT20S'));
-
- sleep(30);
- $cache->prune();
- $this->assertTrue($this->isPruned($cache, 'foo'));
- $this->assertTrue($this->isPruned($cache, 'bar'));
- $this->assertTrue($this->isPruned($cache, 'baz'));
- $this->assertTrue($this->isPruned($cache, 'qux'));
-
- $cache->set('foo', 'foo-val');
- $cache->set('bar', 'bar-val', new \DateInterval('PT20S'));
- $cache->set('baz', 'baz-val', new \DateInterval('PT40S'));
- $cache->set('qux', 'qux-val', new \DateInterval('PT80S'));
-
- $cache->prune();
- $this->assertFalse($this->isPruned($cache, 'foo'));
- $this->assertFalse($this->isPruned($cache, 'bar'));
- $this->assertFalse($this->isPruned($cache, 'baz'));
- $this->assertFalse($this->isPruned($cache, 'qux'));
-
- sleep(30);
- $cache->prune();
- $this->assertFalse($this->isPruned($cache, 'foo'));
- $this->assertTrue($this->isPruned($cache, 'bar'));
- $this->assertFalse($this->isPruned($cache, 'baz'));
- $this->assertFalse($this->isPruned($cache, 'qux'));
-
- sleep(30);
- $cache->prune();
- $this->assertFalse($this->isPruned($cache, 'foo'));
- $this->assertTrue($this->isPruned($cache, 'baz'));
- $this->assertFalse($this->isPruned($cache, 'qux'));
-
- sleep(30);
- $cache->prune();
- $this->assertFalse($this->isPruned($cache, 'foo'));
- $this->assertTrue($this->isPruned($cache, 'qux'));
-
- $cache->clear();
- }
-}
-
-class NotUnserializable
-{
- public function __wakeup()
- {
- throw new \Exception(__CLASS__);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/ChainCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/ChainCacheTest.php
deleted file mode 100644
index 9198c8f46a5df..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/ChainCacheTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\PruneableInterface;
-use Symfony\Component\Cache\Simple\ArrayCache;
-use Symfony\Component\Cache\Simple\ChainCache;
-use Symfony\Component\Cache\Simple\FilesystemCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class ChainCacheTest extends CacheTestCase
-{
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new ChainCache([new ArrayCache($defaultLifetime), new FilesystemCache('', $defaultLifetime)], $defaultLifetime);
- }
-
- public function testEmptyCachesException()
- {
- $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('At least one cache must be specified.');
- new ChainCache([]);
- }
-
- public function testInvalidCacheException()
- {
- $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('The class "stdClass" does not implement');
- new ChainCache([new \stdClass()]);
- }
-
- public function testPrune()
- {
- if (isset($this->skippedTests[__FUNCTION__])) {
- $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
- }
-
- $cache = new ChainCache([
- $this->getPruneableMock(),
- $this->getNonPruneableMock(),
- $this->getPruneableMock(),
- ]);
- $this->assertTrue($cache->prune());
-
- $cache = new ChainCache([
- $this->getPruneableMock(),
- $this->getFailingPruneableMock(),
- $this->getPruneableMock(),
- ]);
- $this->assertFalse($cache->prune());
- }
-
- private function getPruneableMock(): CacheInterface
- {
- $pruneable = $this->createMock([CacheInterface::class, PruneableInterface::class]);
-
- $pruneable
- ->expects($this->atLeastOnce())
- ->method('prune')
- ->willReturn(true);
-
- return $pruneable;
- }
-
- private function getFailingPruneableMock(): CacheInterface
- {
- $pruneable = $this->createMock([CacheInterface::class, PruneableInterface::class]);
-
- $pruneable
- ->expects($this->atLeastOnce())
- ->method('prune')
- ->willReturn(false);
-
- return $pruneable;
- }
-
- private function getNonPruneableMock(): CacheInterface
- {
- return $this->createMock(CacheInterface::class);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php
deleted file mode 100644
index ad7a475d92fdf..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\DoctrineCache;
-use Symfony\Component\Cache\Tests\Fixtures\ArrayCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class DoctrineCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testObjectDoesNotChangeInCache' => 'ArrayCache does not use serialize/unserialize',
- 'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize',
- ];
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new DoctrineCache(new ArrayCache($defaultLifetime), '', $defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/FilesystemCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/FilesystemCacheTest.php
deleted file mode 100644
index b4b7c8ae5b483..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/FilesystemCacheTest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\FilesystemCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class FilesystemCacheTest extends CacheTestCase
-{
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new FilesystemCache('', $defaultLifetime);
- }
-
- protected function isPruned(CacheInterface $cache, string $name): bool
- {
- $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile');
- $getFileMethod->setAccessible(true);
-
- return !file_exists($getFileMethod->invoke($cache, $name));
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/MemcachedCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/MemcachedCacheTest.php
deleted file mode 100644
index 75bf47246c933..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/MemcachedCacheTest.php
+++ /dev/null
@@ -1,176 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Adapter\AbstractAdapter;
-use Symfony\Component\Cache\Simple\MemcachedCache;
-
-/**
- * @group legacy
- */
-class MemcachedCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testSetTtl' => 'Testing expiration slows down the test suite',
- 'testSetMultipleTtl' => 'Testing expiration slows down the test suite',
- 'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
- ];
-
- protected static $client;
-
- public static function setUpBeforeClass(): void
- {
- if (!MemcachedCache::isSupported()) {
- self::markTestSkipped('Extension memcached >=2.2.0 required.');
- }
- self::$client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'));
- self::$client->get('foo');
- $code = self::$client->getResultCode();
-
- if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) {
- self::markTestSkipped('Memcached error: '.strtolower(self::$client->getResultMessage()));
- }
- }
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- $client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['binary_protocol' => false]) : self::$client;
-
- return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime);
- }
-
- public function testCreatePersistentConnectionShouldNotDupServerList()
- {
- $instance = MemcachedCache::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['persistent_id' => 'persistent']);
- $this->assertCount(1, $instance->getServerList());
-
- $instance = MemcachedCache::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['persistent_id' => 'persistent']);
- $this->assertCount(1, $instance->getServerList());
- }
-
- public function testOptions()
- {
- $client = MemcachedCache::createConnection([], [
- 'libketama_compatible' => false,
- 'distribution' => 'modula',
- 'compression' => true,
- 'serializer' => 'php',
- 'hash' => 'md5',
- ]);
-
- $this->assertSame(\Memcached::SERIALIZER_PHP, $client->getOption(\Memcached::OPT_SERIALIZER));
- $this->assertSame(\Memcached::HASH_MD5, $client->getOption(\Memcached::OPT_HASH));
- $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION));
- $this->assertSame(0, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE));
- $this->assertSame(\Memcached::DISTRIBUTION_MODULA, $client->getOption(\Memcached::OPT_DISTRIBUTION));
- }
-
- /**
- * @dataProvider provideBadOptions
- */
- public function testBadOptions(string $name, $value)
- {
- $this->expectException('ErrorException');
- $this->expectExceptionMessage('constant(): Couldn\'t find constant Memcached::');
- MemcachedCache::createConnection([], [$name => $value]);
- }
-
- public function provideBadOptions(): array
- {
- return [
- ['foo', 'bar'],
- ['hash', 'zyx'],
- ['serializer', 'zyx'],
- ['distribution', 'zyx'],
- ];
- }
-
- public function testDefaultOptions()
- {
- $this->assertTrue(MemcachedCache::isSupported());
-
- $client = MemcachedCache::createConnection([]);
-
- $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION));
- $this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL));
- $this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE));
- }
-
- public function testOptionSerializer()
- {
- $this->expectException('Symfony\Component\Cache\Exception\CacheException');
- $this->expectExceptionMessage('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
- if (!\Memcached::HAVE_JSON) {
- $this->markTestSkipped('Memcached::HAVE_JSON required');
- }
-
- new MemcachedCache(MemcachedCache::createConnection([], ['serializer' => 'json']));
- }
-
- /**
- * @dataProvider provideServersSetting
- */
- public function testServersSetting(string $dsn, string $host, int $port)
- {
- $client1 = MemcachedCache::createConnection($dsn);
- $client2 = MemcachedCache::createConnection([$dsn]);
- $client3 = MemcachedCache::createConnection([[$host, $port]]);
- $expect = [
- 'host' => $host,
- 'port' => $port,
- ];
-
- $f = function ($s) { return ['host' => $s['host'], 'port' => $s['port']]; };
- $this->assertSame([$expect], array_map($f, $client1->getServerList()));
- $this->assertSame([$expect], array_map($f, $client2->getServerList()));
- $this->assertSame([$expect], array_map($f, $client3->getServerList()));
- }
-
- public function provideServersSetting(): iterable
- {
- yield [
- 'memcached://127.0.0.1/50',
- '127.0.0.1',
- 11211,
- ];
- yield [
- 'memcached://localhost:11222?weight=25',
- 'localhost',
- 11222,
- ];
- if (filter_var(ini_get('memcached.use_sasl'), FILTER_VALIDATE_BOOLEAN)) {
- yield [
- 'memcached://user:password@127.0.0.1?weight=50',
- '127.0.0.1',
- 11211,
- ];
- }
- yield [
- 'memcached:///var/run/memcached.sock?weight=25',
- '/var/run/memcached.sock',
- 0,
- ];
- yield [
- 'memcached:///var/local/run/memcached.socket?weight=25',
- '/var/local/run/memcached.socket',
- 0,
- ];
- if (filter_var(ini_get('memcached.use_sasl'), FILTER_VALIDATE_BOOLEAN)) {
- yield [
- 'memcached://user:password@/var/local/run/memcached.socket?weight=25',
- '/var/local/run/memcached.socket',
- 0,
- ];
- }
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/MemcachedCacheTextModeTest.php b/src/Symfony/Component/Cache/Tests/Simple/MemcachedCacheTextModeTest.php
deleted file mode 100644
index 57b5b7dd326c2..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/MemcachedCacheTextModeTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Adapter\AbstractAdapter;
-use Symfony\Component\Cache\Simple\MemcachedCache;
-
-/**
- * @group legacy
- */
-class MemcachedCacheTextModeTest extends MemcachedCacheTest
-{
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- $client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['binary_protocol' => false]);
-
- return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/NullCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/NullCacheTest.php
deleted file mode 100644
index ff671bbaccc8e..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/NullCacheTest.php
+++ /dev/null
@@ -1,97 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Cache\Simple\NullCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class NullCacheTest extends TestCase
-{
- public function createCachePool(): NullCache
- {
- return new NullCache();
- }
-
- public function testGetItem()
- {
- $cache = $this->createCachePool();
-
- $this->assertNull($cache->get('key'));
- }
-
- public function testHas()
- {
- $this->assertFalse($this->createCachePool()->has('key'));
- }
-
- public function testGetMultiple()
- {
- $cache = $this->createCachePool();
-
- $keys = ['foo', 'bar', 'baz', 'biz'];
-
- $default = new \stdClass();
- $items = $cache->getMultiple($keys, $default);
- $count = 0;
-
- foreach ($items as $key => $item) {
- $this->assertContains($key, $keys, 'Cache key can not change.');
- $this->assertSame($default, $item);
-
- // Remove $key for $keys
- foreach ($keys as $k => $v) {
- if ($v === $key) {
- unset($keys[$k]);
- }
- }
-
- ++$count;
- }
-
- $this->assertSame(4, $count);
- }
-
- public function testClear()
- {
- $this->assertTrue($this->createCachePool()->clear());
- }
-
- public function testDelete()
- {
- $this->assertTrue($this->createCachePool()->delete('key'));
- }
-
- public function testDeleteMultiple()
- {
- $this->assertTrue($this->createCachePool()->deleteMultiple(['key', 'foo', 'bar']));
- }
-
- public function testSet()
- {
- $cache = $this->createCachePool();
-
- $this->assertFalse($cache->set('key', 'val'));
- $this->assertNull($cache->get('key'));
- }
-
- public function testSetMultiple()
- {
- $cache = $this->createCachePool();
-
- $this->assertFalse($cache->setMultiple(['key' => 'val']));
- $this->assertNull($cache->get('key'));
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/PdoCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/PdoCacheTest.php
deleted file mode 100644
index 204211f06be2d..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/PdoCacheTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\PdoCache;
-use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class PdoCacheTest extends CacheTestCase
-{
- use PdoPruneableTrait;
-
- protected static $dbFile;
-
- 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 PdoCache('sqlite:'.self::$dbFile);
- $pool->createTable();
- }
-
- public static function tearDownAfterClass(): void
- {
- @unlink(self::$dbFile);
- }
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new PdoCache('sqlite:'.self::$dbFile, 'ns', $defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/PdoDbalCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/PdoDbalCacheTest.php
deleted file mode 100644
index 1629959b43434..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/PdoDbalCacheTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Doctrine\DBAL\DriverManager;
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\PdoCache;
-use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class PdoDbalCacheTest extends CacheTestCase
-{
- use PdoPruneableTrait;
-
- protected static $dbFile;
-
- 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 PdoCache(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]));
- $pool->createTable();
- }
-
- public static function tearDownAfterClass(): void
- {
- @unlink(self::$dbFile);
- }
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new PdoCache(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php
deleted file mode 100644
index a504770ac2f30..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php
+++ /dev/null
@@ -1,133 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\NullCache;
-use Symfony\Component\Cache\Simple\PhpArrayCache;
-use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class PhpArrayCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testBasicUsageWithLongKey' => 'PhpArrayCache does no writes',
-
- 'testDelete' => 'PhpArrayCache does no writes',
- 'testDeleteMultiple' => 'PhpArrayCache does no writes',
- 'testDeleteMultipleGenerator' => 'PhpArrayCache does no writes',
-
- 'testSetTtl' => 'PhpArrayCache does no expiration',
- 'testSetMultipleTtl' => 'PhpArrayCache does no expiration',
- 'testSetExpiredTtl' => 'PhpArrayCache does no expiration',
- 'testSetMultipleExpiredTtl' => 'PhpArrayCache does no expiration',
-
- 'testGetInvalidKeys' => 'PhpArrayCache does no validation',
- 'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
- 'testSetInvalidKeys' => 'PhpArrayCache does no validation',
- 'testDeleteInvalidKeys' => 'PhpArrayCache does no validation',
- 'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation',
- 'testSetInvalidTtl' => 'PhpArrayCache does no validation',
- 'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
- 'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation',
- 'testHasInvalidKeys' => 'PhpArrayCache does no validation',
- 'testSetValidData' => 'PhpArrayCache does no validation',
-
- 'testDefaultLifeTime' => 'PhpArrayCache does not allow configuring a default lifetime.',
- 'testPrune' => 'PhpArrayCache just proxies',
- ];
-
- protected static $file;
-
- public static function setUpBeforeClass(): void
- {
- self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php';
- }
-
- protected function tearDown(): void
- {
- $this->createSimpleCache()->clear();
-
- if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
- FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
- }
- }
-
- public function createSimpleCache(): CacheInterface
- {
- return new PhpArrayCacheWrapper(self::$file, new NullCache());
- }
-
- public function testStore()
- {
- $arrayWithRefs = [];
- $arrayWithRefs[0] = 123;
- $arrayWithRefs[1] = &$arrayWithRefs[0];
-
- $object = (object) [
- 'foo' => 'bar',
- 'foo2' => 'bar2',
- ];
-
- $expected = [
- 'null' => null,
- 'serializedString' => serialize($object),
- 'arrayWithRefs' => $arrayWithRefs,
- 'object' => $object,
- 'arrayWithObject' => ['bar' => $object],
- ];
-
- $cache = new PhpArrayCache(self::$file, new NullCache());
- $cache->warmUp($expected);
-
- foreach ($expected as $key => $value) {
- $this->assertSame(serialize($value), serialize($cache->get($key)), 'Warm up should create a PHP file that OPCache can load in memory');
- }
- }
-
- public function testStoredFile()
- {
- $data = [
- 'integer' => 42,
- 'float' => 42.42,
- 'boolean' => true,
- 'array_simple' => ['foo', 'bar'],
- 'array_associative' => ['foo' => 'bar', 'foo2' => 'bar2'],
- ];
- $expected = [
- [
- 'integer' => 0,
- 'float' => 1,
- 'boolean' => 2,
- 'array_simple' => 3,
- 'array_associative' => 4,
- ],
- [
- 0 => 42,
- 1 => 42.42,
- 2 => true,
- 3 => ['foo', 'bar'],
- 4 => ['foo' => 'bar', 'foo2' => 'bar2'],
- ],
- ];
-
- $cache = new PhpArrayCache(self::$file, new NullCache());
- $cache->warmUp($data);
-
- $values = eval(substr(file_get_contents(self::$file), 6));
-
- $this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory');
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php b/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php
deleted file mode 100644
index 84f73012cad24..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\FilesystemCache;
-use Symfony\Component\Cache\Simple\PhpArrayCache;
-use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class PhpArrayCacheWithFallbackTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testGetInvalidKeys' => 'PhpArrayCache does no validation',
- 'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
- 'testDeleteInvalidKeys' => 'PhpArrayCache does no validation',
- 'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation',
- //'testSetValidData' => 'PhpArrayCache does no validation',
- 'testSetInvalidKeys' => 'PhpArrayCache does no validation',
- 'testSetInvalidTtl' => 'PhpArrayCache does no validation',
- 'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
- 'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation',
- 'testHasInvalidKeys' => 'PhpArrayCache does no validation',
- 'testPrune' => 'PhpArrayCache just proxies',
- ];
-
- protected static $file;
-
- public static function setUpBeforeClass(): void
- {
- self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php';
- }
-
- protected function tearDown(): void
- {
- $this->createSimpleCache()->clear();
-
- if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
- FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
- }
- }
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new PhpArrayCache(self::$file, new FilesystemCache('php-array-fallback', $defaultLifetime));
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheWrapper.php b/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheWrapper.php
deleted file mode 100644
index 4dfb2236d5c3d..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheWrapper.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Symfony\Component\Cache\Simple\PhpArrayCache;
-
-class PhpArrayCacheWrapper extends PhpArrayCache
-{
- protected $data = [];
-
- public function set($key, $value, $ttl = null): bool
- {
- (\Closure::bind(function () use ($key, $value) {
- $this->data[$key] = $value;
- $this->warmUp($this->data);
- list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
- }, $this, PhpArrayCache::class))();
-
- return true;
- }
-
- public function setMultiple($values, $ttl = null): bool
- {
- if (!\is_array($values) && !$values instanceof \Traversable) {
- return parent::setMultiple($values, $ttl);
- }
- (\Closure::bind(function () use ($values) {
- foreach ($values as $key => $value) {
- $this->data[$key] = $value;
- }
- $this->warmUp($this->data);
- list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
- }, $this, PhpArrayCache::class))();
-
- return true;
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/PhpFilesCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/PhpFilesCacheTest.php
deleted file mode 100644
index 9698689ac0161..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/PhpFilesCacheTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\PhpFilesCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class PhpFilesCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testDefaultLifeTime' => 'PhpFilesCache does not allow configuring a default lifetime.',
- ];
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new PhpFilesCache('sf-cache');
- }
-
- protected function isPruned(CacheInterface $cache, string $name): bool
- {
- $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile');
- $getFileMethod->setAccessible(true);
-
- return !file_exists($getFileMethod->invoke($cache, $name));
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheTest.php
deleted file mode 100644
index 53bf31d063b1e..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\Cache\CacheItemPoolInterface;
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\Psr6Cache;
-
-/**
- * @group legacy
- */
-abstract class Psr6CacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testPrune' => 'Psr6Cache just proxies',
- ];
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new Psr6Cache($this->createCacheItemPool($defaultLifetime));
- }
-
- abstract protected function createCacheItemPool(int $defaultLifetime = 0): CacheItemPoolInterface;
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheWithAdapterTest.php b/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheWithAdapterTest.php
deleted file mode 100644
index 5d8e8c65e42f5..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheWithAdapterTest.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\Cache\CacheItemPoolInterface;
-use Symfony\Component\Cache\Adapter\FilesystemAdapter;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class Psr6CacheWithAdapterTest extends Psr6CacheTest
-{
- protected function createCacheItemPool(int $defaultLifetime = 0): CacheItemPoolInterface
- {
- return new FilesystemAdapter('', $defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheWithoutAdapterTest.php b/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheWithoutAdapterTest.php
deleted file mode 100644
index ae0f4cf76029c..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/Psr6CacheWithoutAdapterTest.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\Cache\CacheItemPoolInterface;
-use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class Psr6CacheWithoutAdapterTest extends Psr6CacheTest
-{
- protected function createCacheItemPool(int $defaultLifetime = 0): CacheItemPoolInterface
- {
- return new ExternalAdapter($defaultLifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/RedisArrayCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/RedisArrayCacheTest.php
deleted file mode 100644
index 834b6206ac924..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/RedisArrayCacheTest.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-/**
- * @group legacy
- */
-class RedisArrayCacheTest extends AbstractRedisCacheTest
-{
- public static function setUpBeforeClass(): void
- {
- parent::setupBeforeClass();
- if (!class_exists('RedisArray')) {
- self::markTestSkipped('The RedisArray class is required.');
- }
- self::$redis = new \RedisArray([getenv('REDIS_HOST')], ['lazy_connect' => true]);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/RedisCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/RedisCacheTest.php
deleted file mode 100644
index 61a9423978f6f..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/RedisCacheTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Symfony\Component\Cache\Simple\RedisCache;
-
-/**
- * @group legacy
- */
-class RedisCacheTest extends AbstractRedisCacheTest
-{
- public static function setUpBeforeClass(): void
- {
- parent::setupBeforeClass();
- self::$redis = RedisCache::createConnection('redis://'.getenv('REDIS_HOST'));
- }
-
- public function testCreateConnection()
- {
- $redisHost = getenv('REDIS_HOST');
-
- $redis = RedisCache::createConnection('redis://'.$redisHost);
- $this->assertInstanceOf(\Redis::class, $redis);
- $this->assertTrue($redis->isConnected());
- $this->assertSame(0, $redis->getDbNum());
-
- $redis = RedisCache::createConnection('redis://'.$redisHost.'/2');
- $this->assertSame(2, $redis->getDbNum());
-
- $redis = RedisCache::createConnection('redis://'.$redisHost, ['timeout' => 3]);
- $this->assertEquals(3, $redis->getTimeout());
-
- $redis = RedisCache::createConnection('redis://'.$redisHost.'?timeout=4');
- $this->assertEquals(4, $redis->getTimeout());
-
- $redis = RedisCache::createConnection('redis://'.$redisHost, ['read_timeout' => 5]);
- $this->assertEquals(5, $redis->getReadTimeout());
- }
-
- /**
- * @dataProvider provideFailedCreateConnection
- */
- public function testFailedCreateConnection(string $dsn)
- {
- $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Redis connection failed');
- RedisCache::createConnection($dsn);
- }
-
- public function provideFailedCreateConnection(): array
- {
- return [
- ['redis://localhost:1234'],
- ['redis://foo@localhost'],
- ['redis://localhost/123'],
- ];
- }
-
- /**
- * @dataProvider provideInvalidCreateConnection
- */
- public function testInvalidCreateConnection(string $dsn)
- {
- $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Invalid Redis DSN');
- RedisCache::createConnection($dsn);
- }
-
- public function provideInvalidCreateConnection(): array
- {
- return [
- ['foo://localhost'],
- ['redis://'],
- ];
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php
deleted file mode 100644
index c5115c7c70693..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-/**
- * @group legacy
- */
-class RedisClusterCacheTest extends AbstractRedisCacheTest
-{
- public static function setUpBeforeClass(): void
- {
- if (!class_exists('RedisCluster')) {
- self::markTestSkipped('The RedisCluster class is required.');
- }
- if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) {
- self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.');
- }
-
- self::$redis = new \RedisCluster(null, explode(' ', $hosts));
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Simple/TraceableCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/TraceableCacheTest.php
deleted file mode 100644
index 142d437d0bd44..0000000000000
--- a/src/Symfony/Component/Cache/Tests/Simple/TraceableCacheTest.php
+++ /dev/null
@@ -1,173 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Tests\Simple;
-
-use Psr\SimpleCache\CacheInterface;
-use Symfony\Component\Cache\Simple\FilesystemCache;
-use Symfony\Component\Cache\Simple\TraceableCache;
-
-/**
- * @group time-sensitive
- * @group legacy
- */
-class TraceableCacheTest extends CacheTestCase
-{
- protected $skippedTests = [
- 'testPrune' => 'TraceableCache just proxies',
- ];
-
- public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
- {
- return new TraceableCache(new FilesystemCache('', $defaultLifetime));
- }
-
- public function testGetMissTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->get('k');
- $calls = $pool->getCalls();
- $this->assertCount(1, $calls);
-
- $call = $calls[0];
- $this->assertSame('get', $call->name);
- $this->assertSame(['k' => false], $call->result);
- $this->assertSame(0, $call->hits);
- $this->assertSame(1, $call->misses);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testGetHitTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->set('k', 'foo');
- $pool->get('k');
- $calls = $pool->getCalls();
- $this->assertCount(2, $calls);
-
- $call = $calls[1];
- $this->assertSame(1, $call->hits);
- $this->assertSame(0, $call->misses);
- }
-
- public function testGetMultipleMissTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->set('k1', 123);
- $values = $pool->getMultiple(['k0', 'k1']);
- foreach ($values as $value) {
- }
- $calls = $pool->getCalls();
- $this->assertCount(2, $calls);
-
- $call = $calls[1];
- $this->assertSame('getMultiple', $call->name);
- $this->assertSame(['k1' => true, 'k0' => false], $call->result);
- $this->assertSame(1, $call->misses);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testHasMissTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->has('k');
- $calls = $pool->getCalls();
- $this->assertCount(1, $calls);
-
- $call = $calls[0];
- $this->assertSame('has', $call->name);
- $this->assertSame(['k' => false], $call->result);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testHasHitTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->set('k', 'foo');
- $pool->has('k');
- $calls = $pool->getCalls();
- $this->assertCount(2, $calls);
-
- $call = $calls[1];
- $this->assertSame('has', $call->name);
- $this->assertSame(['k' => true], $call->result);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testDeleteTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->delete('k');
- $calls = $pool->getCalls();
- $this->assertCount(1, $calls);
-
- $call = $calls[0];
- $this->assertSame('delete', $call->name);
- $this->assertSame(['k' => true], $call->result);
- $this->assertSame(0, $call->hits);
- $this->assertSame(0, $call->misses);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testDeleteMultipleTrace()
- {
- $pool = $this->createSimpleCache();
- $arg = ['k0', 'k1'];
- $pool->deleteMultiple($arg);
- $calls = $pool->getCalls();
- $this->assertCount(1, $calls);
-
- $call = $calls[0];
- $this->assertSame('deleteMultiple', $call->name);
- $this->assertSame(['keys' => $arg, 'result' => true], $call->result);
- $this->assertSame(0, $call->hits);
- $this->assertSame(0, $call->misses);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testTraceSetTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->set('k', 'foo');
- $calls = $pool->getCalls();
- $this->assertCount(1, $calls);
-
- $call = $calls[0];
- $this->assertSame('set', $call->name);
- $this->assertSame(['k' => true], $call->result);
- $this->assertSame(0, $call->hits);
- $this->assertSame(0, $call->misses);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-
- public function testSetMultipleTrace()
- {
- $pool = $this->createSimpleCache();
- $pool->setMultiple(['k' => 'foo']);
- $calls = $pool->getCalls();
- $this->assertCount(1, $calls);
-
- $call = $calls[0];
- $this->assertSame('setMultiple', $call->name);
- $this->assertSame(['keys' => ['k'], 'result' => true], $call->result);
- $this->assertSame(0, $call->hits);
- $this->assertSame(0, $call->misses);
- $this->assertNotEmpty($call->start);
- $this->assertNotEmpty($call->end);
- }
-}
diff --git a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
index 7f1544af5b41d..c981530e9efc2 100644
--- a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
+++ b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
@@ -131,20 +131,6 @@ public function testTagItemExpiry()
$this->assertFalse($pool->getItem('foo')->isHit());
}
- /**
- * @group legacy
- */
- public function testGetPreviousTags()
- {
- $pool = $this->createCachePool();
-
- $i = $pool->getItem('k');
- $pool->save($i->tag('foo'));
-
- $i = $pool->getItem('k');
- $this->assertSame(['foo' => 'foo'], $i->getPreviousTags());
- }
-
public function testGetMetadata()
{
$pool = $this->createCachePool();
diff --git a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php
index 8bf9354dfbc49..f9d73c30f53ee 100644
--- a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php
+++ b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Cache\Traits;
use Psr\Cache\CacheItemInterface;
+use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Cache\CacheItem;
/**
@@ -21,7 +22,7 @@
*/
trait AbstractAdapterTrait
{
- use AbstractTrait;
+ use LoggerAwareTrait;
/**
* @var \Closure needs to be set by class, signature is function(string , mixed , bool )
@@ -33,6 +34,173 @@ trait AbstractAdapterTrait
*/
private $mergeByLifetime;
+ private $namespace;
+ private $namespaceVersion = '';
+ private $versioningIsEnabled = false;
+ private $deferred = [];
+ private $ids = [];
+
+ /**
+ * @var int|null The maximum length to enforce for identifiers or null when no limit applies
+ */
+ protected $maxIdLength;
+
+ /**
+ * Fetches several cache items.
+ *
+ * @param array $ids The cache identifiers to fetch
+ *
+ * @return array|\Traversable The corresponding values found in the cache
+ */
+ abstract protected function doFetch(array $ids);
+
+ /**
+ * Confirms if the cache contains specified cache item.
+ *
+ * @param string $id The identifier for which to check existence
+ *
+ * @return bool True if item exists in the cache, false otherwise
+ */
+ abstract protected function doHave(string $id);
+
+ /**
+ * Deletes all items in the pool.
+ *
+ * @param string $namespace The prefix used for all identifiers managed by this pool
+ *
+ * @return bool True if the pool was successfully cleared, false otherwise
+ */
+ abstract protected function doClear(string $namespace);
+
+ /**
+ * Removes multiple items from the pool.
+ *
+ * @param array $ids An array of identifiers that should be removed from the pool
+ *
+ * @return bool True if the items were successfully removed, false otherwise
+ */
+ abstract protected function doDelete(array $ids);
+
+ /**
+ * Persists several cache items immediately.
+ *
+ * @param array $values The values to cache, indexed by their cache identifier
+ * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
+ *
+ * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not
+ */
+ abstract protected function doSave(array $values, int $lifetime);
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function hasItem($key)
+ {
+ $id = $this->getId($key);
+
+ if (isset($this->deferred[$key])) {
+ $this->commit();
+ }
+
+ try {
+ return $this->doHave($id);
+ } catch (\Exception $e) {
+ CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached: '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
+
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function clear(string $prefix = '')
+ {
+ $this->deferred = [];
+ if ($cleared = $this->versioningIsEnabled) {
+ if ('' === $namespaceVersionToClear = $this->namespaceVersion) {
+ foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
+ $namespaceVersionToClear = $v;
+ }
+ }
+ $namespaceToClear = $this->namespace.$namespaceVersionToClear;
+ $namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5);
+ try {
+ $cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0);
+ } catch (\Exception $e) {
+ $cleared = false;
+ }
+ if ($cleared = true === $cleared || [] === $cleared) {
+ $this->namespaceVersion = $namespaceVersion;
+ $this->ids = [];
+ }
+ } else {
+ $namespaceToClear = $this->namespace.$prefix;
+ }
+
+ try {
+ return $this->doClear($namespaceToClear) || $cleared;
+ } catch (\Exception $e) {
+ CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e]);
+
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function deleteItem($key)
+ {
+ return $this->deleteItems([$key]);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function deleteItems(array $keys)
+ {
+ $ids = [];
+
+ foreach ($keys as $key) {
+ $ids[$key] = $this->getId($key);
+ unset($this->deferred[$key]);
+ }
+
+ try {
+ if ($this->doDelete($ids)) {
+ return true;
+ }
+ } catch (\Exception $e) {
+ }
+
+ $ok = true;
+
+ // When bulk-delete failed, retry each item individually
+ foreach ($ids as $key => $id) {
+ try {
+ $e = null;
+ if ($this->doDelete([$id])) {
+ continue;
+ }
+ } catch (\Exception $e) {
+ }
+ $message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
+ CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
+ $ok = false;
+ }
+
+ return $ok;
+ }
+
/**
* {@inheritdoc}
*/
@@ -114,6 +282,40 @@ public function saveDeferred(CacheItemInterface $item)
return true;
}
+ /**
+ * Enables/disables versioning of items.
+ *
+ * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed,
+ * but old keys may need garbage collection and extra round-trips to the back-end are required.
+ *
+ * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it.
+ *
+ * @param bool $enable
+ *
+ * @return bool the previous state of versioning
+ */
+ public function enableVersioning($enable = true)
+ {
+ $wasEnabled = $this->versioningIsEnabled;
+ $this->versioningIsEnabled = (bool) $enable;
+ $this->namespaceVersion = '';
+ $this->ids = [];
+
+ return $wasEnabled;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reset()
+ {
+ if ($this->deferred) {
+ $this->commit();
+ }
+ $this->namespaceVersion = '';
+ $this->ids = [];
+ }
+
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
@@ -152,4 +354,47 @@ private function generateItems(iterable $items, array &$keys): iterable
yield $key => $f($key, null, false);
}
}
+
+ private function getId($key)
+ {
+ if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
+ $this->ids = [];
+ $this->namespaceVersion = '1'.static::NS_SEPARATOR;
+ try {
+ foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
+ $this->namespaceVersion = $v;
+ }
+ if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) {
+ $this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::NS_SEPARATOR, 5);
+ $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0);
+ }
+ } catch (\Exception $e) {
+ }
+ }
+
+ if (\is_string($key) && isset($this->ids[$key])) {
+ return $this->namespace.$this->namespaceVersion.$this->ids[$key];
+ }
+ CacheItem::validateKey($key);
+ $this->ids[$key] = $key;
+
+ if (null === $this->maxIdLength) {
+ return $this->namespace.$this->namespaceVersion.$key;
+ }
+ if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
+ // Use MD5 to favor speed over security, which is not an issue here
+ $this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2));
+ $id = $this->namespace.$this->namespaceVersion.$id;
+ }
+
+ return $id;
+ }
+
+ /**
+ * @internal
+ */
+ public static function handleUnserializeCallback($class)
+ {
+ throw new \DomainException('Class not found: '.$class);
+ }
}
diff --git a/src/Symfony/Component/Cache/Traits/AbstractTrait.php b/src/Symfony/Component/Cache/Traits/AbstractTrait.php
deleted file mode 100644
index 7639424ea78ce..0000000000000
--- a/src/Symfony/Component/Cache/Traits/AbstractTrait.php
+++ /dev/null
@@ -1,303 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Psr\Log\LoggerAwareTrait;
-use Symfony\Component\Cache\CacheItem;
-
-/**
- * @author Nicolas Grekas
- *
- * @internal
- */
-trait AbstractTrait
-{
- use LoggerAwareTrait;
-
- private $namespace;
- private $namespaceVersion = '';
- private $versioningIsEnabled = false;
- private $deferred = [];
- private $ids = [];
-
- /**
- * @var int|null The maximum length to enforce for identifiers or null when no limit applies
- */
- protected $maxIdLength;
-
- /**
- * Fetches several cache items.
- *
- * @param array $ids The cache identifiers to fetch
- *
- * @return array|\Traversable The corresponding values found in the cache
- */
- abstract protected function doFetch(array $ids);
-
- /**
- * Confirms if the cache contains specified cache item.
- *
- * @param string $id The identifier for which to check existence
- *
- * @return bool True if item exists in the cache, false otherwise
- */
- abstract protected function doHave($id);
-
- /**
- * Deletes all items in the pool.
- *
- * @param string $namespace The prefix used for all identifiers managed by this pool
- *
- * @return bool True if the pool was successfully cleared, false otherwise
- */
- abstract protected function doClear($namespace);
-
- /**
- * Removes multiple items from the pool.
- *
- * @param array $ids An array of identifiers that should be removed from the pool
- *
- * @return bool True if the items were successfully removed, false otherwise
- */
- abstract protected function doDelete(array $ids);
-
- /**
- * Persists several cache items immediately.
- *
- * @param array $values The values to cache, indexed by their cache identifier
- * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
- *
- * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not
- */
- abstract protected function doSave(array $values, $lifetime);
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function hasItem($key)
- {
- $id = $this->getId($key);
-
- if (isset($this->deferred[$key])) {
- $this->commit();
- }
-
- try {
- return $this->doHave($id);
- } catch (\Exception $e) {
- CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached: '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
-
- return false;
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @param string $prefix
- *
- * @return bool
- */
- public function clear(/*string $prefix = ''*/)
- {
- $this->deferred = [];
- if ($cleared = $this->versioningIsEnabled) {
- if ('' === $namespaceVersionToClear = $this->namespaceVersion) {
- foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
- $namespaceVersionToClear = $v;
- }
- }
- $namespaceToClear = $this->namespace.$namespaceVersionToClear;
- $namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5);
- try {
- $cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0);
- } catch (\Exception $e) {
- $cleared = false;
- }
- if ($cleared = true === $cleared || [] === $cleared) {
- $this->namespaceVersion = $namespaceVersion;
- $this->ids = [];
- }
- } else {
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
- $namespaceToClear = $this->namespace.$prefix;
- }
-
- try {
- return $this->doClear($namespaceToClear) || $cleared;
- } catch (\Exception $e) {
- CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e]);
-
- return false;
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteItem($key)
- {
- return $this->deleteItems([$key]);
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteItems(array $keys)
- {
- $ids = [];
-
- foreach ($keys as $key) {
- $ids[$key] = $this->getId($key);
- unset($this->deferred[$key]);
- }
-
- try {
- if ($this->doDelete($ids)) {
- return true;
- }
- } catch (\Exception $e) {
- }
-
- $ok = true;
-
- // When bulk-delete failed, retry each item individually
- foreach ($ids as $key => $id) {
- try {
- $e = null;
- if ($this->doDelete([$id])) {
- continue;
- }
- } catch (\Exception $e) {
- }
- $message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
- CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
- $ok = false;
- }
-
- return $ok;
- }
-
- /**
- * Enables/disables versioning of items.
- *
- * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed,
- * but old keys may need garbage collection and extra round-trips to the back-end are required.
- *
- * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it.
- *
- * @param bool $enable
- *
- * @return bool the previous state of versioning
- */
- public function enableVersioning($enable = true)
- {
- $wasEnabled = $this->versioningIsEnabled;
- $this->versioningIsEnabled = (bool) $enable;
- $this->namespaceVersion = '';
- $this->ids = [];
-
- return $wasEnabled;
- }
-
- /**
- * {@inheritdoc}
- */
- public function reset()
- {
- if ($this->deferred) {
- $this->commit();
- }
- $this->namespaceVersion = '';
- $this->ids = [];
- }
-
- /**
- * Like the native unserialize() function but throws an exception if anything goes wrong.
- *
- * @param string $value
- *
- * @return mixed
- *
- * @throws \Exception
- *
- * @deprecated since Symfony 4.2, use DefaultMarshaller instead.
- */
- protected static function unserialize($value)
- {
- @trigger_error(sprintf('The "%s::unserialize()" method is deprecated since Symfony 4.2, use DefaultMarshaller instead.', __CLASS__), E_USER_DEPRECATED);
-
- if ('b:0;' === $value) {
- return false;
- }
- $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
- try {
- if (false !== $value = unserialize($value)) {
- return $value;
- }
- throw new \DomainException('Failed to unserialize cached value');
- } catch (\Error $e) {
- throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- } finally {
- ini_set('unserialize_callback_func', $unserializeCallbackHandler);
- }
- }
-
- private function getId($key): string
- {
- if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
- $this->ids = [];
- $this->namespaceVersion = '1'.static::NS_SEPARATOR;
- try {
- foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
- $this->namespaceVersion = $v;
- }
- if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) {
- $this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::NS_SEPARATOR, 5);
- $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0);
- }
- } catch (\Exception $e) {
- }
- }
-
- if (\is_string($key) && isset($this->ids[$key])) {
- return $this->namespace.$this->namespaceVersion.$this->ids[$key];
- }
- CacheItem::validateKey($key);
- $this->ids[$key] = $key;
-
- if (null === $this->maxIdLength) {
- return $this->namespace.$this->namespaceVersion.$key;
- }
- if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
- // Use MD5 to favor speed over security, which is not an issue here
- $this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2));
- $id = $this->namespace.$this->namespaceVersion.$id;
- }
-
- return $id;
- }
-
- /**
- * @internal
- */
- public static function handleUnserializeCallback($class)
- {
- throw new \DomainException('Class not found: '.$class);
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/ApcuTrait.php b/src/Symfony/Component/Cache/Traits/ApcuTrait.php
deleted file mode 100644
index c55def6671a8d..0000000000000
--- a/src/Symfony/Component/Cache/Traits/ApcuTrait.php
+++ /dev/null
@@ -1,121 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Symfony\Component\Cache\CacheItem;
-use Symfony\Component\Cache\Exception\CacheException;
-
-/**
- * @author Nicolas Grekas
- *
- * @internal
- */
-trait ApcuTrait
-{
- public static function isSupported()
- {
- return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN);
- }
-
- private function init(string $namespace, int $defaultLifetime, ?string $version)
- {
- if (!static::isSupported()) {
- throw new CacheException('APCu is not enabled');
- }
- if ('cli' === \PHP_SAPI) {
- ini_set('apc.use_request_time', 0);
- }
- parent::__construct($namespace, $defaultLifetime);
-
- if (null !== $version) {
- CacheItem::validateKey($version);
-
- if (!apcu_exists($version.'@'.$namespace)) {
- $this->doClear($namespace);
- apcu_add($version.'@'.$namespace, null);
- }
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doFetch(array $ids)
- {
- $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
- try {
- $values = [];
- foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) {
- if (null !== $v || $ok) {
- $values[$k] = $v;
- }
- }
-
- return $values;
- } catch (\Error $e) {
- throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- } finally {
- ini_set('unserialize_callback_func', $unserializeCallbackHandler);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doHave($id)
- {
- return apcu_exists($id);
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doClear($namespace)
- {
- return isset($namespace[0]) && class_exists('APCuIterator', false) && ('cli' !== \PHP_SAPI || filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN))
- ? apcu_delete(new \APCuIterator(sprintf('/^%s/', preg_quote($namespace, '/')), APC_ITER_KEY))
- : apcu_clear_cache();
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doDelete(array $ids)
- {
- foreach ($ids as $id) {
- apcu_delete($id);
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doSave(array $values, $lifetime)
- {
- try {
- if (false === $failures = apcu_store($values, null, $lifetime)) {
- $failures = $values;
- }
-
- return array_keys($failures);
- } catch (\Throwable $e) {
- if (1 === \count($values)) {
- // Workaround https://github.com/krakjoe/apcu/issues/170
- apcu_delete(key($values));
- }
-
- throw $e;
- }
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/ArrayTrait.php b/src/Symfony/Component/Cache/Traits/ArrayTrait.php
deleted file mode 100644
index 21872c515b423..0000000000000
--- a/src/Symfony/Component/Cache/Traits/ArrayTrait.php
+++ /dev/null
@@ -1,183 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Psr\Log\LoggerAwareTrait;
-use Symfony\Component\Cache\CacheItem;
-
-/**
- * @author Nicolas Grekas
- *
- * @internal
- */
-trait ArrayTrait
-{
- use LoggerAwareTrait;
-
- private $storeSerialized;
- private $values = [];
- private $expiries = [];
-
- /**
- * Returns all cached values, with cache miss as null.
- *
- * @return array
- */
- public function getValues()
- {
- if (!$this->storeSerialized) {
- return $this->values;
- }
-
- $values = $this->values;
- foreach ($values as $k => $v) {
- if (null === $v || 'N;' === $v) {
- continue;
- }
- if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
- $values[$k] = serialize($v);
- }
- }
-
- return $values;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function hasItem($key)
- {
- if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
- return true;
- }
- CacheItem::validateKey($key);
-
- return isset($this->expiries[$key]) && !$this->deleteItem($key);
- }
-
- /**
- * {@inheritdoc}
- *
- * @param string $prefix
- *
- * @return bool
- */
- public function clear(/*string $prefix = ''*/)
- {
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
-
- if ('' !== $prefix) {
- foreach ($this->values as $key => $value) {
- if (0 === strpos($key, $prefix)) {
- unset($this->values[$key], $this->expiries[$key]);
- }
- }
- } else {
- $this->values = $this->expiries = [];
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function deleteItem($key)
- {
- if (!\is_string($key) || !isset($this->expiries[$key])) {
- CacheItem::validateKey($key);
- }
- unset($this->values[$key], $this->expiries[$key]);
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function reset()
- {
- $this->clear();
- }
-
- private function generateItems(array $keys, float $now, callable $f): iterable
- {
- foreach ($keys as $i => $key) {
- if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
- $this->values[$key] = $value = null;
- } else {
- $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
- }
- unset($keys[$i]);
-
- yield $key => $f($key, $value, $isHit);
- }
-
- foreach ($keys as $key) {
- yield $key => $f($key, null, false);
- }
- }
-
- private function freeze($value, $key)
- {
- if (null === $value) {
- return 'N;';
- }
- if (\is_string($value)) {
- // Serialize strings if they could be confused with serialized objects or arrays
- if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
- return serialize($value);
- }
- } elseif (!is_scalar($value)) {
- try {
- $serialized = serialize($value);
- } catch (\Exception $e) {
- $type = \is_object($value) ? \get_class($value) : \gettype($value);
- $message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage());
- CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
-
- return null;
- }
- // Keep value serialized if it contains any objects or any internal references
- if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
- return $serialized;
- }
- }
-
- return $value;
- }
-
- private function unfreeze(string $key, bool &$isHit)
- {
- if ('N;' === $value = $this->values[$key]) {
- return null;
- }
- if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
- try {
- $value = unserialize($value);
- } catch (\Exception $e) {
- CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
- $value = false;
- }
- if (false === $value) {
- $this->values[$key] = $value = null;
- $isHit = false;
- }
- }
-
- return $value;
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/DoctrineTrait.php b/src/Symfony/Component/Cache/Traits/DoctrineTrait.php
deleted file mode 100644
index c87ecabafc803..0000000000000
--- a/src/Symfony/Component/Cache/Traits/DoctrineTrait.php
+++ /dev/null
@@ -1,98 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-/**
- * @author Nicolas Grekas
- *
- * @internal
- */
-trait DoctrineTrait
-{
- private $provider;
-
- /**
- * {@inheritdoc}
- */
- public function reset()
- {
- parent::reset();
- $this->provider->setNamespace($this->provider->getNamespace());
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doFetch(array $ids)
- {
- $unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback');
- try {
- return $this->provider->fetchMultiple($ids);
- } catch (\Error $e) {
- $trace = $e->getTrace();
-
- if (isset($trace[0]['function']) && !isset($trace[0]['class'])) {
- switch ($trace[0]['function']) {
- case 'unserialize':
- case 'apcu_fetch':
- case 'apc_fetch':
- throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- }
- }
-
- throw $e;
- } finally {
- ini_set('unserialize_callback_func', $unserializeCallbackHandler);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doHave($id)
- {
- return $this->provider->contains($id);
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doClear($namespace)
- {
- $namespace = $this->provider->getNamespace();
-
- return isset($namespace[0])
- ? $this->provider->deleteAll()
- : $this->provider->flushAll();
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doDelete(array $ids)
- {
- $ok = true;
- foreach ($ids as $id) {
- $ok = $this->provider->delete($id) && $ok;
- }
-
- return $ok;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doSave(array $values, $lifetime)
- {
- return $this->provider->saveMultiple($values, $lifetime);
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
index 15cc950e81a9b..86ab270cd8e7d 100644
--- a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
+++ b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
@@ -53,7 +53,7 @@ private function init(string $namespace, ?string $directory)
/**
* {@inheritdoc}
*/
- protected function doClear($namespace)
+ protected function doClear(string $namespace)
{
$ok = true;
diff --git a/src/Symfony/Component/Cache/Traits/FilesystemTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemTrait.php
index 185eb0006d657..d6926c79630bd 100644
--- a/src/Symfony/Component/Cache/Traits/FilesystemTrait.php
+++ b/src/Symfony/Component/Cache/Traits/FilesystemTrait.php
@@ -81,7 +81,7 @@ protected function doFetch(array $ids)
/**
* {@inheritdoc}
*/
- protected function doHave($id)
+ protected function doHave(string $id)
{
$file = $this->getFile($id);
@@ -91,7 +91,7 @@ protected function doHave($id)
/**
* {@inheritdoc}
*/
- protected function doSave(array $values, $lifetime)
+ protected function doSave(array $values, int $lifetime)
{
$expiresAt = $lifetime ? (time() + $lifetime) : 0;
$values = $this->marshaller->marshall($values, $failed);
diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
deleted file mode 100644
index 070eb0e173593..0000000000000
--- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
+++ /dev/null
@@ -1,325 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Symfony\Component\Cache\Exception\CacheException;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
-use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-
-/**
- * @author Rob Frawley 2nd
- * @author Nicolas Grekas
- *
- * @internal
- */
-trait MemcachedTrait
-{
- private static $defaultClientOptions = [
- 'persistent_id' => null,
- 'username' => null,
- 'password' => null,
- \Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP,
- ];
-
- private $marshaller;
- private $client;
- private $lazyClient;
-
- public static function isSupported()
- {
- return \extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
- }
-
- private function init(\Memcached $client, string $namespace, int $defaultLifetime, ?MarshallerInterface $marshaller)
- {
- if (!static::isSupported()) {
- throw new CacheException('Memcached >= 2.2.0 is required');
- }
- if ('Memcached' === \get_class($client)) {
- $opt = $client->getOption(\Memcached::OPT_SERIALIZER);
- if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
- throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
- }
- $this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY));
- $this->client = $client;
- } else {
- $this->lazyClient = $client;
- }
-
- parent::__construct($namespace, $defaultLifetime);
- $this->enableVersioning();
- $this->marshaller = $marshaller ?? new DefaultMarshaller();
- }
-
- /**
- * Creates a Memcached instance.
- *
- * By default, the binary protocol, no block, and libketama compatible options are enabled.
- *
- * Examples for servers:
- * - 'memcached://user:pass@localhost?weight=33'
- * - [['localhost', 11211, 33]]
- *
- * @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs
- *
- * @return \Memcached
- *
- * @throws \ErrorException When invalid options or servers are provided
- */
- public static function createConnection($servers, array $options = [])
- {
- if (\is_string($servers)) {
- $servers = [$servers];
- } elseif (!\is_array($servers)) {
- throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, %s given.', \gettype($servers)));
- }
- if (!static::isSupported()) {
- throw new CacheException('Memcached >= 2.2.0 is required');
- }
- set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
- try {
- $options += static::$defaultClientOptions;
- $client = new \Memcached($options['persistent_id']);
- $username = $options['username'];
- $password = $options['password'];
-
- // parse any DSN in $servers
- foreach ($servers as $i => $dsn) {
- if (\is_array($dsn)) {
- continue;
- }
- if (0 !== strpos($dsn, 'memcached:')) {
- throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached:"', $dsn));
- }
- $params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
- if (!empty($m[2])) {
- list($username, $password) = explode(':', $m[2], 2) + [1 => null];
- }
-
- return 'file:'.($m[1] ?? '');
- }, $dsn);
- if (false === $params = parse_url($params)) {
- throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
- }
- $query = $hosts = [];
- if (isset($params['query'])) {
- parse_str($params['query'], $query);
-
- if (isset($query['host'])) {
- if (!\is_array($hosts = $query['host'])) {
- throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
- }
- foreach ($hosts as $host => $weight) {
- if (false === $port = strrpos($host, ':')) {
- $hosts[$host] = [$host, 11211, (int) $weight];
- } else {
- $hosts[$host] = [substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight];
- }
- }
- $hosts = array_values($hosts);
- unset($query['host']);
- }
- if ($hosts && !isset($params['host']) && !isset($params['path'])) {
- unset($servers[$i]);
- $servers = array_merge($servers, $hosts);
- continue;
- }
- }
- if (!isset($params['host']) && !isset($params['path'])) {
- throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
- }
- if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) {
- $params['weight'] = $m[1];
- $params['path'] = substr($params['path'], 0, -\strlen($m[0]));
- }
- $params += [
- 'host' => isset($params['host']) ? $params['host'] : $params['path'],
- 'port' => isset($params['host']) ? 11211 : null,
- 'weight' => 0,
- ];
- if ($query) {
- $params += $query;
- $options = $query + $options;
- }
-
- $servers[$i] = [$params['host'], $params['port'], $params['weight']];
-
- if ($hosts) {
- $servers = array_merge($servers, $hosts);
- }
- }
-
- // set client's options
- unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']);
- $options = array_change_key_case($options, CASE_UPPER);
- $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
- $client->setOption(\Memcached::OPT_NO_BLOCK, true);
- $client->setOption(\Memcached::OPT_TCP_NODELAY, true);
- if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
- $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
- }
- foreach ($options as $name => $value) {
- if (\is_int($name)) {
- continue;
- }
- if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) {
- $value = \constant('Memcached::'.$name.'_'.strtoupper($value));
- }
- $opt = \constant('Memcached::OPT_'.$name);
-
- unset($options[$name]);
- $options[$opt] = $value;
- }
- $client->setOptions($options);
-
- // set client's servers, taking care of persistent connections
- if (!$client->isPristine()) {
- $oldServers = [];
- foreach ($client->getServerList() as $server) {
- $oldServers[] = [$server['host'], $server['port']];
- }
-
- $newServers = [];
- foreach ($servers as $server) {
- if (1 < \count($server)) {
- $server = array_values($server);
- unset($server[2]);
- $server[1] = (int) $server[1];
- }
- $newServers[] = $server;
- }
-
- if ($oldServers !== $newServers) {
- $client->resetServerList();
- $client->addServers($servers);
- }
- } else {
- $client->addServers($servers);
- }
-
- if (null !== $username || null !== $password) {
- if (!method_exists($client, 'setSaslAuthData')) {
- trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
- }
- $client->setSaslAuthData($username, $password);
- }
-
- return $client;
- } finally {
- restore_error_handler();
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doSave(array $values, $lifetime)
- {
- if (!$values = $this->marshaller->marshall($values, $failed)) {
- return $failed;
- }
-
- if ($lifetime && $lifetime > 30 * 86400) {
- $lifetime += time();
- }
-
- $encodedValues = [];
- foreach ($values as $key => $value) {
- $encodedValues[rawurlencode($key)] = $value;
- }
-
- return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)) ? $failed : false;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doFetch(array $ids)
- {
- try {
- $encodedIds = array_map('rawurlencode', $ids);
-
- $encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds));
-
- $result = [];
- foreach ($encodedResult as $key => $value) {
- $result[rawurldecode($key)] = $this->marshaller->unmarshall($value);
- }
-
- return $result;
- } catch (\Error $e) {
- throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doHave($id)
- {
- return false !== $this->getClient()->get(rawurlencode($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode());
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doDelete(array $ids)
- {
- $ok = true;
- $encodedIds = array_map('rawurlencode', $ids);
- foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) {
- if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) {
- $ok = false;
- break;
- }
- }
-
- return $ok;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doClear($namespace)
- {
- return '' === $namespace && $this->getClient()->flush();
- }
-
- private function checkResultCode($result)
- {
- $code = $this->client->getResultCode();
-
- if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) {
- return $result;
- }
-
- throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage())));
- }
-
- private function getClient(): \Memcached
- {
- if ($this->client) {
- return $this->client;
- }
-
- $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER);
- if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
- throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
- }
- if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) {
- throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix));
- }
-
- return $this->client = $this->lazyClient;
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/PdoTrait.php b/src/Symfony/Component/Cache/Traits/PdoTrait.php
deleted file mode 100644
index f927b4ec47d31..0000000000000
--- a/src/Symfony/Component/Cache/Traits/PdoTrait.php
+++ /dev/null
@@ -1,446 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\DBALException;
-use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
-use Doctrine\DBAL\DriverManager;
-use Doctrine\DBAL\Exception\TableNotFoundException;
-use Doctrine\DBAL\Schema\Schema;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
-use Symfony\Component\Cache\Marshaller\MarshallerInterface;
-
-/**
- * @internal
- */
-trait PdoTrait
-{
- private $marshaller;
- private $conn;
- private $dsn;
- private $driver;
- private $serverVersion;
- private $table = 'cache_items';
- private $idCol = 'item_id';
- private $dataCol = 'item_data';
- private $lifetimeCol = 'item_lifetime';
- private $timeCol = 'item_time';
- private $username = '';
- private $password = '';
- private $connectionOptions = [];
- private $namespace;
-
- private function init($connOrDsn, string $namespace, int $defaultLifetime, array $options, ?MarshallerInterface $marshaller)
- {
- if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
- throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
- }
-
- if ($connOrDsn instanceof \PDO) {
- if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
- throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
- }
-
- $this->conn = $connOrDsn;
- } elseif ($connOrDsn instanceof Connection) {
- $this->conn = $connOrDsn;
- } elseif (\is_string($connOrDsn)) {
- $this->dsn = $connOrDsn;
- } else {
- throw new InvalidArgumentException(sprintf('"%s" requires PDO or Doctrine\DBAL\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, \is_object($connOrDsn) ? \get_class($connOrDsn) : \gettype($connOrDsn)));
- }
-
- $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table;
- $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol;
- $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol;
- $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol;
- $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol;
- $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username;
- $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password;
- $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions;
- $this->namespace = $namespace;
- $this->marshaller = $marshaller ?? new DefaultMarshaller();
-
- parent::__construct($namespace, $defaultLifetime);
- }
-
- /**
- * Creates the table to store cache items which can be called once for setup.
- *
- * Cache ID are saved in a column of maximum length 255. Cache data is
- * saved in a BLOB.
- *
- * @throws \PDOException When the table already exists
- * @throws DBALException When the table already exists
- * @throws \DomainException When an unsupported PDO driver is used
- */
- public function createTable()
- {
- // connect if we are not yet
- $conn = $this->getConnection();
-
- if ($conn instanceof Connection) {
- $types = [
- 'mysql' => 'binary',
- 'sqlite' => 'text',
- 'pgsql' => 'string',
- 'oci' => 'string',
- 'sqlsrv' => 'string',
- ];
- if (!isset($types[$this->driver])) {
- throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
- }
-
- $schema = new Schema();
- $table = $schema->createTable($this->table);
- $table->addColumn($this->idCol, $types[$this->driver], ['length' => 255]);
- $table->addColumn($this->dataCol, 'blob', ['length' => 16777215]);
- $table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]);
- $table->addColumn($this->timeCol, 'integer', ['unsigned' => true]);
- $table->setPrimaryKey([$this->idCol]);
-
- foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
- $conn->exec($sql);
- }
-
- return;
- }
-
- switch ($this->driver) {
- case 'mysql':
- // We use varbinary for the ID column because it prevents unwanted conversions:
- // - character set conversions between server and client
- // - trailing space removal
- // - case-insensitivity
- // - language processing like é == e
- $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(255) NOT NULL PRIMARY KEY, $this->dataCol MEDIUMBLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
- break;
- case 'sqlite':
- $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
- break;
- case 'pgsql':
- $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
- break;
- case 'oci':
- $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(255) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
- break;
- case 'sqlsrv':
- $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
- break;
- default:
- throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
- }
-
- $conn->exec($sql);
- }
-
- /**
- * {@inheritdoc}
- */
- public function prune()
- {
- $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time";
-
- if ('' !== $this->namespace) {
- $deleteSql .= " AND $this->idCol LIKE :namespace";
- }
-
- try {
- $delete = $this->getConnection()->prepare($deleteSql);
- } catch (TableNotFoundException $e) {
- return true;
- } catch (\PDOException $e) {
- return true;
- }
- $delete->bindValue(':time', time(), \PDO::PARAM_INT);
-
- if ('' !== $this->namespace) {
- $delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR);
- }
- try {
- return $delete->execute();
- } catch (TableNotFoundException $e) {
- return true;
- } catch (\PDOException $e) {
- return true;
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doFetch(array $ids)
- {
- $now = time();
- $expired = [];
-
- $sql = str_pad('', (\count($ids) << 1) - 1, '?,');
- $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN ($sql)";
- $stmt = $this->getConnection()->prepare($sql);
- $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
- foreach ($ids as $id) {
- $stmt->bindValue(++$i, $id);
- }
- $stmt->execute();
-
- while ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
- if (null === $row[1]) {
- $expired[] = $row[0];
- } else {
- yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]);
- }
- }
-
- if ($expired) {
- $sql = str_pad('', (\count($expired) << 1) - 1, '?,');
- $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN ($sql)";
- $stmt = $this->getConnection()->prepare($sql);
- $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
- foreach ($expired as $id) {
- $stmt->bindValue(++$i, $id);
- }
- $stmt->execute();
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doHave($id)
- {
- $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = :id AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > :time)";
- $stmt = $this->getConnection()->prepare($sql);
-
- $stmt->bindValue(':id', $id);
- $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
- $stmt->execute();
-
- return (bool) $stmt->fetchColumn();
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doClear($namespace)
- {
- $conn = $this->getConnection();
-
- if ('' === $namespace) {
- if ('sqlite' === $this->driver) {
- $sql = "DELETE FROM $this->table";
- } else {
- $sql = "TRUNCATE TABLE $this->table";
- }
- } else {
- $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
- }
-
- try {
- $conn->exec($sql);
- } catch (TableNotFoundException $e) {
- } catch (\PDOException $e) {
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doDelete(array $ids)
- {
- $sql = str_pad('', (\count($ids) << 1) - 1, '?,');
- $sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)";
- try {
- $stmt = $this->getConnection()->prepare($sql);
- $stmt->execute(array_values($ids));
- } catch (TableNotFoundException $e) {
- } catch (\PDOException $e) {
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doSave(array $values, $lifetime)
- {
- if (!$values = $this->marshaller->marshall($values, $failed)) {
- return $failed;
- }
-
- $conn = $this->getConnection();
- $driver = $this->driver;
- $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
-
- switch (true) {
- case 'mysql' === $driver:
- $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
- break;
- case 'oci' === $driver:
- // DUAL is Oracle specific dummy table
- $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ".
- "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
- "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?";
- break;
- case 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '10', '>='):
- // MERGE is only available since SQL Server 2008 and must be terminated by semicolon
- // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
- $sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
- "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
- "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
- break;
- case 'sqlite' === $driver:
- $sql = 'INSERT OR REPLACE'.substr($insertSql, 6);
- break;
- case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='):
- $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
- break;
- default:
- $driver = null;
- $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
- break;
- }
-
- $now = time();
- $lifetime = $lifetime ?: null;
- try {
- $stmt = $conn->prepare($sql);
- } catch (TableNotFoundException $e) {
- if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
- $this->createTable();
- }
- $stmt = $conn->prepare($sql);
- } catch (\PDOException $e) {
- if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
- $this->createTable();
- }
- $stmt = $conn->prepare($sql);
- }
-
- if ('sqlsrv' === $driver || 'oci' === $driver) {
- $stmt->bindParam(1, $id);
- $stmt->bindParam(2, $id);
- $stmt->bindParam(3, $data, \PDO::PARAM_LOB);
- $stmt->bindValue(4, $lifetime, \PDO::PARAM_INT);
- $stmt->bindValue(5, $now, \PDO::PARAM_INT);
- $stmt->bindParam(6, $data, \PDO::PARAM_LOB);
- $stmt->bindValue(7, $lifetime, \PDO::PARAM_INT);
- $stmt->bindValue(8, $now, \PDO::PARAM_INT);
- } else {
- $stmt->bindParam(':id', $id);
- $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
- $stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
- $stmt->bindValue(':time', $now, \PDO::PARAM_INT);
- }
- if (null === $driver) {
- $insertStmt = $conn->prepare($insertSql);
-
- $insertStmt->bindParam(':id', $id);
- $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
- $insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
- $insertStmt->bindValue(':time', $now, \PDO::PARAM_INT);
- }
-
- foreach ($values as $id => $data) {
- try {
- $stmt->execute();
- } catch (TableNotFoundException $e) {
- if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
- $this->createTable();
- }
- $stmt->execute();
- } catch (\PDOException $e) {
- if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
- $this->createTable();
- }
- $stmt->execute();
- }
- if (null === $driver && !$stmt->rowCount()) {
- try {
- $insertStmt->execute();
- } catch (DBALException $e) {
- } catch (\PDOException $e) {
- // A concurrent write won, let it be
- }
- }
- }
-
- return $failed;
- }
-
- /**
- * @return \PDO|Connection
- */
- private function getConnection()
- {
- if (null === $this->conn) {
- if (strpos($this->dsn, '://')) {
- if (!class_exists(DriverManager::class)) {
- throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $this->dsn));
- }
- $this->conn = DriverManager::getConnection(['url' => $this->dsn]);
- } else {
- $this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions);
- $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
- }
- }
- if (null === $this->driver) {
- if ($this->conn instanceof \PDO) {
- $this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME);
- } else {
- switch ($this->driver = $this->conn->getDriver()->getName()) {
- case 'mysqli':
- throw new \LogicException(sprintf('The adapter "%s" does not support the mysqli driver, use pdo_mysql instead.', static::class));
- case 'pdo_mysql':
- case 'drizzle_pdo_mysql':
- $this->driver = 'mysql';
- break;
- case 'pdo_sqlite':
- $this->driver = 'sqlite';
- break;
- case 'pdo_pgsql':
- $this->driver = 'pgsql';
- break;
- case 'oci8':
- case 'pdo_oracle':
- $this->driver = 'oci';
- break;
- case 'pdo_sqlsrv':
- $this->driver = 'sqlsrv';
- break;
- }
- }
- }
-
- return $this->conn;
- }
-
- private function getServerVersion(): string
- {
- if (null === $this->serverVersion) {
- $conn = $this->conn instanceof \PDO ? $this->conn : $this->conn->getWrappedConnection();
- if ($conn instanceof \PDO) {
- $this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
- } elseif ($conn instanceof ServerInfoAwareConnection) {
- $this->serverVersion = $conn->getServerVersion();
- } else {
- $this->serverVersion = '0';
- }
- }
-
- return $this->serverVersion;
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php
deleted file mode 100644
index 6e7c72ca7d93a..0000000000000
--- a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php
+++ /dev/null
@@ -1,169 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Symfony\Component\Cache\Adapter\AdapterInterface;
-use Symfony\Component\Cache\CacheItem;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\VarExporter\VarExporter;
-
-/**
- * @author Titouan Galopin
- * @author Nicolas Grekas
- *
- * @internal
- */
-trait PhpArrayTrait
-{
- use ProxyTrait;
-
- private $file;
- private $keys;
- private $values;
-
- private static $valuesCache = [];
-
- /**
- * Store an array of cached values.
- *
- * @param array $values The cached values
- */
- public function warmUp(array $values)
- {
- if (file_exists($this->file)) {
- if (!is_file($this->file)) {
- throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s.', $this->file));
- }
-
- if (!is_writable($this->file)) {
- throw new InvalidArgumentException(sprintf('Cache file is not writable: %s.', $this->file));
- }
- } else {
- $directory = \dirname($this->file);
-
- if (!is_dir($directory) && !@mkdir($directory, 0777, true)) {
- throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s.', $directory));
- }
-
- if (!is_writable($directory)) {
- throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s.', $directory));
- }
- }
-
- $dumpedValues = '';
- $dumpedMap = [];
- $dump = <<<'EOF'
- $value) {
- CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
- $isStaticValue = true;
-
- if (null === $value) {
- $value = "'N;'";
- } elseif (\is_object($value) || \is_array($value)) {
- try {
- $value = VarExporter::export($value, $isStaticValue);
- } catch (\Exception $e) {
- throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
- }
- } elseif (\is_string($value)) {
- // Wrap "N;" in a closure to not confuse it with an encoded `null`
- if ('N;' === $value) {
- $isStaticValue = false;
- }
- $value = var_export($value, true);
- } elseif (!is_scalar($value)) {
- throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
- } else {
- $value = var_export($value, true);
- }
-
- if (!$isStaticValue) {
- $value = str_replace("\n", "\n ", $value);
- $value = "static function () {\n return {$value};\n}";
- }
- $hash = hash('md5', $value);
-
- if (null === $id = $dumpedMap[$hash] ?? null) {
- $id = $dumpedMap[$hash] = \count($dumpedMap);
- $dumpedValues .= "{$id} => {$value},\n";
- }
-
- $dump .= var_export($key, true)." => {$id},\n";
- }
-
- $dump .= "\n], [\n\n{$dumpedValues}\n]];\n";
-
- $tmpFile = uniqid($this->file, true);
-
- file_put_contents($tmpFile, $dump);
- @chmod($tmpFile, 0666 & ~umask());
- unset($serialized, $value, $dump);
-
- @rename($tmpFile, $this->file);
- unset(self::$valuesCache[$this->file]);
-
- $this->initialize();
- }
-
- /**
- * {@inheritdoc}
- *
- * @param string $prefix
- *
- * @return bool
- */
- public function clear(/*string $prefix = ''*/)
- {
- $prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
- $this->keys = $this->values = [];
-
- $cleared = @unlink($this->file) || !file_exists($this->file);
- unset(self::$valuesCache[$this->file]);
-
- if ($this->pool instanceof AdapterInterface) {
- return $this->pool->clear($prefix) && $cleared;
- }
-
- return $this->pool->clear() && $cleared;
- }
-
- /**
- * Load the cache file.
- */
- private function initialize()
- {
- if (isset(self::$valuesCache[$this->file])) {
- $values = self::$valuesCache[$this->file];
- } elseif (!file_exists($this->file)) {
- $this->keys = $this->values = [];
-
- return;
- } else {
- $values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []];
- }
-
- if (2 !== \count($values) || !isset($values[0], $values[1])) {
- $this->keys = $this->values = [];
- } else {
- list($this->keys, $this->values) = $values;
- }
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php
deleted file mode 100644
index 05b9d8843394d..0000000000000
--- a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php
+++ /dev/null
@@ -1,313 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Cache\Traits;
-
-use Symfony\Component\Cache\Exception\CacheException;
-use Symfony\Component\Cache\Exception\InvalidArgumentException;
-use Symfony\Component\VarExporter\VarExporter;
-
-/**
- * @author Piotr Stankowski
- * @author Nicolas Grekas
- * @author Rob Frawley 2nd
- *
- * @internal
- */
-trait PhpFilesTrait
-{
- use FilesystemCommonTrait {
- doClear as private doCommonClear;
- doDelete as private doCommonDelete;
- }
-
- private $includeHandler;
- private $appendOnly;
- private $values = [];
- private $files = [];
-
- private static $startTime;
- private static $valuesCache = [];
-
- public static function isSupported()
- {
- self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
-
- return \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN));
- }
-
- /**
- * @return bool
- */
- public function prune()
- {
- $time = time();
- $pruned = true;
- $getExpiry = true;
-
- set_error_handler($this->includeHandler);
- try {
- foreach ($this->scanHashDir($this->directory) as $file) {
- try {
- if (\is_array($expiresAt = include $file)) {
- $expiresAt = $expiresAt[0];
- }
- } catch (\ErrorException $e) {
- $expiresAt = $time;
- }
-
- if ($time >= $expiresAt) {
- $pruned = $this->doUnlink($file) && !file_exists($file) && $pruned;
- }
- }
- } finally {
- restore_error_handler();
- }
-
- return $pruned;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doFetch(array $ids)
- {
- if ($this->appendOnly) {
- $now = 0;
- $missingIds = [];
- } else {
- $now = time();
- $missingIds = $ids;
- $ids = [];
- }
- $values = [];
-
- begin:
- $getExpiry = false;
-
- foreach ($ids as $id) {
- if (null === $value = $this->values[$id] ?? null) {
- $missingIds[] = $id;
- } elseif ('N;' === $value) {
- $values[$id] = null;
- } elseif (!\is_object($value)) {
- $values[$id] = $value;
- } elseif (!$value instanceof LazyValue) {
- $values[$id] = $value();
- } elseif (false === $values[$id] = include $value->file) {
- unset($values[$id], $this->values[$id]);
- $missingIds[] = $id;
- }
- if (!$this->appendOnly) {
- unset($this->values[$id]);
- }
- }
-
- if (!$missingIds) {
- return $values;
- }
-
- set_error_handler($this->includeHandler);
- try {
- $getExpiry = true;
-
- foreach ($missingIds as $k => $id) {
- try {
- $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
-
- if (isset(self::$valuesCache[$file])) {
- [$expiresAt, $this->values[$id]] = self::$valuesCache[$file];
- } elseif (\is_array($expiresAt = include $file)) {
- if ($this->appendOnly) {
- self::$valuesCache[$file] = $expiresAt;
- }
-
- [$expiresAt, $this->values[$id]] = $expiresAt;
- } elseif ($now < $expiresAt) {
- $this->values[$id] = new LazyValue($file);
- }
-
- if ($now >= $expiresAt) {
- unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
- }
- } catch (\ErrorException $e) {
- unset($missingIds[$k]);
- }
- }
- } finally {
- restore_error_handler();
- }
-
- $ids = $missingIds;
- $missingIds = [];
- goto begin;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doHave($id)
- {
- if ($this->appendOnly && isset($this->values[$id])) {
- return true;
- }
-
- set_error_handler($this->includeHandler);
- try {
- $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
- $getExpiry = true;
-
- if (isset(self::$valuesCache[$file])) {
- [$expiresAt, $value] = self::$valuesCache[$file];
- } elseif (\is_array($expiresAt = include $file)) {
- if ($this->appendOnly) {
- self::$valuesCache[$file] = $expiresAt;
- }
-
- [$expiresAt, $value] = $expiresAt;
- } elseif ($this->appendOnly) {
- $value = new LazyValue($file);
- }
- } catch (\ErrorException $e) {
- return false;
- } finally {
- restore_error_handler();
- }
- if ($this->appendOnly) {
- $now = 0;
- $this->values[$id] = $value;
- } else {
- $now = time();
- }
-
- return $now < $expiresAt;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doSave(array $values, $lifetime)
- {
- $ok = true;
- $expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX';
- $allowCompile = self::isSupported();
-
- foreach ($values as $key => $value) {
- unset($this->values[$key]);
- $isStaticValue = true;
- if (null === $value) {
- $value = "'N;'";
- } elseif (\is_object($value) || \is_array($value)) {
- try {
- $value = VarExporter::export($value, $isStaticValue);
- } catch (\Exception $e) {
- throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
- }
- } elseif (\is_string($value)) {
- // Wrap "N;" in a closure to not confuse it with an encoded `null`
- if ('N;' === $value) {
- $isStaticValue = false;
- }
- $value = var_export($value, true);
- } elseif (!is_scalar($value)) {
- throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
- } else {
- $value = var_export($value, true);
- }
-
- $encodedKey = rawurlencode($key);
-
- if ($isStaticValue) {
- $value = "return [{$expiry}, {$value}];";
- } elseif ($this->appendOnly) {
- $value = "return [{$expiry}, static function () { return {$value}; }];";
- } else {
- // We cannot use a closure here because of https://bugs.php.net/76982
- $value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
- $value = "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
- }
-
- $file = $this->files[$key] = $this->getFile($key, true);
- // Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
- $ok = $this->write($file, "directory)) {
- throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory));
- }
-
- return $ok;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doClear($namespace)
- {
- $this->values = [];
-
- return $this->doCommonClear($namespace);
- }
-
- /**
- * {@inheritdoc}
- */
- protected function doDelete(array $ids)
- {
- foreach ($ids as $id) {
- unset($this->values[$id]);
- }
-
- return $this->doCommonDelete($ids);
- }
-
- protected function doUnlink($file)
- {
- unset(self::$valuesCache[$file]);
-
- if (self::isSupported()) {
- @opcache_invalidate($file, true);
- }
-
- return @unlink($file);
- }
-
- private function getFileKey(string $file): string
- {
- if (!$h = @fopen($file, 'rb')) {
- return '';
- }
-
- $encodedKey = substr(fgets($h), 8);
- fclose($h);
-
- return rawurldecode(rtrim($encodedKey));
- }
-}
-
-/**
- * @internal
- */
-class LazyValue
-{
- public $file;
-
- public function __construct(string $file)
- {
- $this->file = $file;
- }
-}
diff --git a/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php b/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php
index b4cef59a3e2c3..73c6a4fdbb55e 100644
--- a/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php
+++ b/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php
@@ -26,7 +26,7 @@ public function __construct(\Closure $initializer)
$this->initializer = $initializer;
}
- public function __call($method, array $args)
+ public function __call(string $method, array $args)
{
$this->redis ?: $this->redis = $this->initializer->__invoke();
diff --git a/src/Symfony/Component/Cache/Traits/RedisProxy.php b/src/Symfony/Component/Cache/Traits/RedisProxy.php
index 2b0b85736720b..ec5cfabb3381c 100644
--- a/src/Symfony/Component/Cache/Traits/RedisProxy.php
+++ b/src/Symfony/Component/Cache/Traits/RedisProxy.php
@@ -28,7 +28,7 @@ public function __construct(\Redis $redis, \Closure $initializer)
$this->initializer = $initializer;
}
- public function __call($method, array $args)
+ public function __call(string $method, array $args)
{
$this->ready ?: $this->ready = $this->initializer->__invoke($this->redis);
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index 9b65ccb987f0e..5a8460a0d2824 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -329,7 +329,7 @@ protected function doFetch(array $ids)
/**
* {@inheritdoc}
*/
- protected function doHave($id)
+ protected function doHave(string $id)
{
return (bool) $this->redis->exists($id);
}
@@ -337,7 +337,7 @@ protected function doHave($id)
/**
* {@inheritdoc}
*/
- protected function doClear($namespace)
+ protected function doClear(string $namespace)
{
$cleared = true;
if ($this->redis instanceof \Predis\ClientInterface) {
@@ -404,7 +404,7 @@ protected function doDelete(array $ids)
/**
* {@inheritdoc}
*/
- protected function doSave(array $values, $lifetime)
+ protected function doSave(array $values, int $lifetime)
{
if (!$values = $this->marshaller->marshall($values, $failed)) {
return $failed;
diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json
index 0f0033c40844b..a049e04ebb198 100644
--- a/src/Symfony/Component/Cache/composer.json
+++ b/src/Symfony/Component/Cache/composer.json
@@ -21,12 +21,12 @@
"symfony/cache-implementation": "1.0"
},
"require": {
- "php": "^7.1.3",
+ "php": "^7.2.5",
"psr/cache": "~1.0",
"psr/log": "~1.0",
"symfony/cache-contracts": "^1.1.7|^2",
"symfony/service-contracts": "^1.1|^2",
- "symfony/var-exporter": "^4.2|^5.0"
+ "symfony/var-exporter": "^4.4|^5.0"
},
"require-dev": {
"cache/integration-tests": "dev-master",
@@ -34,13 +34,13 @@
"doctrine/dbal": "~2.5",
"predis/predis": "~1.1",
"psr/simple-cache": "^1.0",
- "symfony/config": "^4.2|^5.0",
- "symfony/dependency-injection": "^3.4|^4.1|^5.0",
+ "symfony/config": "^4.4|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
"symfony/var-dumper": "^4.4|^5.0"
},
"conflict": {
"doctrine/dbal": "<2.5",
- "symfony/dependency-injection": "<3.4",
+ "symfony/dependency-injection": "<4.4",
"symfony/http-kernel": "<4.4",
"symfony/var-dumper": "<4.4"
},
@@ -53,7 +53,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Cache/phpunit.xml.dist b/src/Symfony/Component/Cache/phpunit.xml.dist
index 591046cf1c41c..0ad6430f0b409 100644
--- a/src/Symfony/Component/Cache/phpunit.xml.dist
+++ b/src/Symfony/Component/Cache/phpunit.xml.dist
@@ -12,6 +12,9 @@
+
+
+
diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md
index a650e10ab8ff3..14743e474bf00 100644
--- a/src/Symfony/Component/Config/CHANGELOG.md
+++ b/src/Symfony/Component/Config/CHANGELOG.md
@@ -1,6 +1,14 @@
CHANGELOG
=========
+5.0.0
+-----
+
+ * Dropped support for constructing a `TreeBuilder` without passing root node information.
+ * Removed the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead
+ * Added method `getChildNodeDefinitions()` to ParentNodeDefinitionInterface
+ * Removed `FileLoaderLoadException`, use `LoaderLoadException` instead
+
4.4.0
-----
diff --git a/src/Symfony/Component/Config/ConfigCacheFactory.php b/src/Symfony/Component/Config/ConfigCacheFactory.php
index bfb70cb2ceda5..11fd3cb3a2d45 100644
--- a/src/Symfony/Component/Config/ConfigCacheFactory.php
+++ b/src/Symfony/Component/Config/ConfigCacheFactory.php
@@ -35,12 +35,8 @@ public function __construct(bool $debug)
/**
* {@inheritdoc}
*/
- public function cache($file, $callback)
+ public function cache(string $file, callable $callback)
{
- if (!\is_callable($callback)) {
- throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback)));
- }
-
$cache = new ConfigCache($file, $this->debug);
if (!$cache->isFresh()) {
$callback($cache);
diff --git a/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php b/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php
index 8e80142b7816d..7dfa0f2437972 100644
--- a/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php
+++ b/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php
@@ -28,5 +28,5 @@ interface ConfigCacheFactoryInterface
*
* @return ConfigCacheInterface The cache instance
*/
- public function cache($file, $callable);
+ public function cache(string $file, callable $callable);
}
diff --git a/src/Symfony/Component/Config/ConfigCacheInterface.php b/src/Symfony/Component/Config/ConfigCacheInterface.php
index 7c47ad70a5685..001e7e7c105e8 100644
--- a/src/Symfony/Component/Config/ConfigCacheInterface.php
+++ b/src/Symfony/Component/Config/ConfigCacheInterface.php
@@ -45,5 +45,5 @@ public function isFresh();
*
* @throws \RuntimeException When the cache file cannot be written
*/
- public function write($content, array $metadata = null);
+ public function write(string $content, array $metadata = null);
}
diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php
index 3b2ace70645fe..7f6a5331c5eac 100644
--- a/src/Symfony/Component/Config/Definition/ArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/ArrayNode.php
@@ -98,42 +98,34 @@ public function getXmlRemappings()
/**
* Sets whether to add default values for this array if it has not been
* defined in any of the configuration files.
- *
- * @param bool $boolean
*/
- public function setAddIfNotSet($boolean)
+ public function setAddIfNotSet(bool $boolean)
{
- $this->addIfNotSet = (bool) $boolean;
+ $this->addIfNotSet = $boolean;
}
/**
* Sets whether false is allowed as value indicating that the array should be unset.
- *
- * @param bool $allow
*/
- public function setAllowFalse($allow)
+ public function setAllowFalse(bool $allow)
{
- $this->allowFalse = (bool) $allow;
+ $this->allowFalse = $allow;
}
/**
* Sets whether new keys can be defined in subsequent configurations.
- *
- * @param bool $allow
*/
- public function setAllowNewKeys($allow)
+ public function setAllowNewKeys(bool $allow)
{
- $this->allowNewKeys = (bool) $allow;
+ $this->allowNewKeys = $allow;
}
/**
* Sets if deep merging should occur.
- *
- * @param bool $boolean
*/
- public function setPerformDeepMerging($boolean)
+ public function setPerformDeepMerging(bool $boolean)
{
- $this->performDeepMerging = (bool) $boolean;
+ $this->performDeepMerging = $boolean;
}
/**
@@ -142,16 +134,16 @@ public function setPerformDeepMerging($boolean)
* @param bool $boolean To allow extra keys
* @param bool $remove To remove extra keys
*/
- public function setIgnoreExtraKeys($boolean, $remove = true)
+ public function setIgnoreExtraKeys(bool $boolean, bool $remove = true)
{
- $this->ignoreExtraKeys = (bool) $boolean;
+ $this->ignoreExtraKeys = $boolean;
$this->removeExtraKeys = $this->ignoreExtraKeys && $remove;
}
/**
* {@inheritdoc}
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -235,7 +227,7 @@ protected function finalizeValue($value)
}
if ($child->isDeprecated()) {
- @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
+ trigger_deprecation('', '', $child->getDeprecationMessage($name, $this->getPath()));
}
try {
@@ -336,11 +328,9 @@ protected function normalizeValue($value)
/**
* Remaps multiple singular values to a single plural value.
*
- * @param array $value The source values
- *
* @return array The remapped values
*/
- protected function remapXml($value)
+ protected function remapXml(array $value)
{
foreach ($this->xmlRemappings as list($singular, $plural)) {
if (!isset($value[$singular])) {
diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php
index 33dd92781065e..72325d8480a14 100644
--- a/src/Symfony/Component/Config/Definition/BaseNode.php
+++ b/src/Symfony/Component/Config/Definition/BaseNode.php
@@ -97,30 +97,23 @@ public static function resetPlaceholders(): void
self::$placeholders = [];
}
- /**
- * @param string $key
- */
- public function setAttribute($key, $value)
+ public function setAttribute(string $key, $value)
{
$this->attributes[$key] = $value;
}
/**
- * @param string $key
- *
* @return mixed
*/
- public function getAttribute($key, $default = null)
+ public function getAttribute(string $key, $default = null)
{
return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
}
/**
- * @param string $key
- *
* @return bool
*/
- public function hasAttribute($key)
+ public function hasAttribute(string $key)
{
return isset($this->attributes[$key]);
}
@@ -138,20 +131,15 @@ public function setAttributes(array $attributes)
$this->attributes = $attributes;
}
- /**
- * @param string $key
- */
- public function removeAttribute($key)
+ public function removeAttribute(string $key)
{
unset($this->attributes[$key]);
}
/**
* Sets an info message.
- *
- * @param string $info
*/
- public function setInfo($info)
+ public function setInfo(string $info)
{
$this->setAttribute('info', $info);
}
@@ -202,9 +190,9 @@ public function addEquivalentValue($originalValue, $equivalentValue)
*
* @param bool $boolean Required node
*/
- public function setRequired($boolean)
+ public function setRequired(bool $boolean)
{
- $this->required = (bool) $boolean;
+ $this->required = $boolean;
}
/**
@@ -212,22 +200,18 @@ public function setRequired($boolean)
*
* You can use %node% and %path% placeholders in your message to display,
* respectively, the node name and its complete path.
- *
- * @param string|null $message Deprecated message
*/
- public function setDeprecated($message)
+ public function setDeprecated(?string $message)
{
$this->deprecationMessage = $message;
}
/**
* Sets if this node can be overridden.
- *
- * @param bool $allow
*/
- public function setAllowOverwrite($allow)
+ public function setAllowOverwrite(bool $allow)
{
- $this->allowOverwrite = (bool) $allow;
+ $this->allowOverwrite = $allow;
}
/**
@@ -276,7 +260,7 @@ public function isDeprecated()
*
* @return string
*/
- public function getDeprecationMessage($node, $path)
+ public function getDeprecationMessage(string $node, string $path)
{
return strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]);
}
diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
index e40d97b2fb89c..fdac03acca0df 100644
--- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
+++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
@@ -66,11 +66,9 @@ public function children()
/**
* Sets a prototype for child nodes.
*
- * @param string $type The type of node
- *
* @return NodeDefinition
*/
- public function prototype($type)
+ public function prototype(string $type)
{
return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this);
}
@@ -194,12 +192,12 @@ public function disallowNewKeysInSubsequentConfigs()
/**
* Sets a normalization rule for XML configurations.
*
- * @param string $singular The key to remap
- * @param string $plural The plural of the key for irregular plurals
+ * @param string $singular The key to remap
+ * @param string|null $plural The plural of the key for irregular plurals
*
* @return $this
*/
- public function fixXmlConfig($singular, $plural = null)
+ public function fixXmlConfig(string $singular, string $plural = null)
{
$this->normalization()->remap($singular, $plural);
@@ -234,7 +232,7 @@ public function fixXmlConfig($singular, $plural = null)
*
* @return $this
*/
- public function useAttributeAsKey($name, $removeKeyItem = true)
+ public function useAttributeAsKey(string $name, bool $removeKeyItem = true)
{
$this->key = $name;
$this->removeKeyItem = $removeKeyItem;
@@ -245,11 +243,9 @@ public function useAttributeAsKey($name, $removeKeyItem = true)
/**
* Sets whether the node can be unset.
*
- * @param bool $allow
- *
* @return $this
*/
- public function canBeUnset($allow = true)
+ public function canBeUnset(bool $allow = true)
{
$this->merge()->allowUnset($allow);
@@ -341,7 +337,7 @@ public function performNoDeepMerging()
*
* @return $this
*/
- public function ignoreExtraKeys($remove = true)
+ public function ignoreExtraKeys(bool $remove = true)
{
$this->ignoreExtraKeys = true;
$this->removeExtraKeys = $remove;
@@ -350,15 +346,13 @@ public function ignoreExtraKeys($remove = true)
}
/**
- * Sets key normalization.
- *
- * @param bool $bool Whether to enable key normalization
+ * Sets whether to enable key normalization.
*
* @return $this
*/
- public function normalizeKeys($bool)
+ public function normalizeKeys(bool $bool)
{
- $this->normalizeKeys = (bool) $bool;
+ $this->normalizeKeys = $bool;
return $this;
}
@@ -417,6 +411,10 @@ protected function createNode()
}
if ($this->default) {
+ if (!\is_array($this->defaultValue)) {
+ throw new \InvalidArgumentException(sprintf('%s: the default value of an array node has to be an array.', $node->getPath()));
+ }
+
$node->setDefaultValue($this->defaultValue);
}
diff --git a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php
index 05949d2b5a3b1..4d918cef12b31 100644
--- a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php
+++ b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php
@@ -178,13 +178,11 @@ public function thenEmptyArray()
*
* if you want to add the value of the node in your message just use a %s placeholder.
*
- * @param string $message
- *
* @return $this
*
* @throws \InvalidArgumentException
*/
- public function thenInvalid($message)
+ public function thenInvalid(string $message)
{
$this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };
diff --git a/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php
index 105e2d64709b1..a88d49ba93a41 100644
--- a/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php
+++ b/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php
@@ -30,11 +30,9 @@ public function __construct(NodeDefinition $node)
/**
* Sets whether the node can be unset.
*
- * @param bool $allow
- *
* @return $this
*/
- public function allowUnset($allow = true)
+ public function allowUnset(bool $allow = true)
{
$this->allowFalse = $allow;
@@ -44,11 +42,9 @@ public function allowUnset($allow = true)
/**
* Sets whether the node can be overwritten.
*
- * @param bool $deny Whether the overwriting is forbidden or not
- *
* @return $this
*/
- public function denyOverwrite($deny = true)
+ public function denyOverwrite(bool $deny = true)
{
$this->allowOverwrite = !$deny;
diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php
index be059f5d17fce..bab439b9ba2db 100644
--- a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php
+++ b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php
@@ -49,11 +49,9 @@ public function setParent(ParentNodeDefinitionInterface $parent = null)
/**
* Creates a child array node.
*
- * @param string $name The name of the node
- *
* @return ArrayNodeDefinition The child node
*/
- public function arrayNode($name)
+ public function arrayNode(string $name)
{
return $this->node($name, 'array');
}
@@ -61,11 +59,9 @@ public function arrayNode($name)
/**
* Creates a child scalar node.
*
- * @param string $name The name of the node
- *
* @return ScalarNodeDefinition The child node
*/
- public function scalarNode($name)
+ public function scalarNode(string $name)
{
return $this->node($name, 'scalar');
}
@@ -73,11 +69,9 @@ public function scalarNode($name)
/**
* Creates a child Boolean node.
*
- * @param string $name The name of the node
- *
* @return BooleanNodeDefinition The child node
*/
- public function booleanNode($name)
+ public function booleanNode(string $name)
{
return $this->node($name, 'boolean');
}
@@ -85,11 +79,9 @@ public function booleanNode($name)
/**
* Creates a child integer node.
*
- * @param string $name The name of the node
- *
* @return IntegerNodeDefinition The child node
*/
- public function integerNode($name)
+ public function integerNode(string $name)
{
return $this->node($name, 'integer');
}
@@ -97,11 +89,9 @@ public function integerNode($name)
/**
* Creates a child float node.
*
- * @param string $name The name of the node
- *
* @return FloatNodeDefinition The child node
*/
- public function floatNode($name)
+ public function floatNode(string $name)
{
return $this->node($name, 'float');
}
@@ -109,11 +99,9 @@ public function floatNode($name)
/**
* Creates a child EnumNode.
*
- * @param string $name
- *
* @return EnumNodeDefinition
*/
- public function enumNode($name)
+ public function enumNode(string $name)
{
return $this->node($name, 'enum');
}
@@ -121,11 +109,9 @@ public function enumNode($name)
/**
* Creates a child variable node.
*
- * @param string $name The name of the node
- *
* @return VariableNodeDefinition The builder of the child node
*/
- public function variableNode($name)
+ public function variableNode(string $name)
{
return $this->node($name, 'variable');
}
@@ -143,15 +129,12 @@ public function end()
/**
* Creates a child node.
*
- * @param string|null $name The name of the node
- * @param string $type The type of the node
- *
* @return NodeDefinition The child node
*
* @throws \RuntimeException When the node type is not registered
* @throws \RuntimeException When the node class is not found
*/
- public function node($name, $type)
+ public function node(?string $name, string $type)
{
$class = $this->getNodeClass($type);
@@ -202,7 +185,7 @@ public function append(NodeDefinition $node)
*
* @return $this
*/
- public function setNodeClass($type, $class)
+ public function setNodeClass(string $type, string $class)
{
$this->nodeMapping[strtolower($type)] = $class;
@@ -212,14 +195,12 @@ public function setNodeClass($type, $class)
/**
* Returns the class name of the node definition.
*
- * @param string $type The node type
- *
* @return string The node definition class name
*
* @throws \RuntimeException When the node type is not registered
* @throws \RuntimeException When the node class is not found
*/
- protected function getNodeClass($type)
+ protected function getNodeClass(string $type)
{
$type = strtolower($type);
diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php
index 61cb081c9731f..36519908c0b25 100644
--- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php
+++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php
@@ -59,11 +59,9 @@ public function setParent(NodeParentInterface $parent)
/**
* Sets info message.
*
- * @param string $info The info text
- *
* @return $this
*/
- public function info($info)
+ public function info(string $info)
{
return $this->attribute('info', $info);
}
@@ -83,12 +81,11 @@ public function example($example)
/**
* Sets an attribute on the node.
*
- * @param string $key
- * @param mixed $value
+ * @param mixed $value
*
* @return $this
*/
- public function attribute($key, $value)
+ public function attribute(string $key, $value)
{
$this->attributes[$key] = $value;
@@ -112,7 +109,7 @@ public function end()
*
* @return NodeInterface
*/
- public function getNode($forceRootNode = false)
+ public function getNode(bool $forceRootNode = false)
{
if ($forceRootNode) {
$this->parent = null;
@@ -165,11 +162,9 @@ public function isRequired()
* You can use %node% and %path% placeholders in your message to display,
* respectively, the node name and its complete path.
*
- * @param string $message Deprecation message
- *
* @return $this
*/
- public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.')
+ public function setDeprecated(string $message = 'The child node "%node%" at path "%path%" is deprecated.')
{
$this->deprecationMessage = $message;
@@ -287,11 +282,9 @@ public function validate()
/**
* Sets whether the node can be overwritten.
*
- * @param bool $deny Whether the overwriting is forbidden or not
- *
* @return $this
*/
- public function cannotBeOverwritten($deny = true)
+ public function cannotBeOverwritten(bool $deny = true)
{
$this->merge()->denyOverwrite($deny);
@@ -357,12 +350,8 @@ abstract protected function createNode();
public function setPathSeparator(string $separator)
{
if ($this instanceof ParentNodeDefinitionInterface) {
- if (method_exists($this, 'getChildNodeDefinitions')) {
- foreach ($this->getChildNodeDefinitions() as $child) {
- $child->setPathSeparator($separator);
- }
- } else {
- @trigger_error(sprintf('Not implementing the "%s::getChildNodeDefinitions()" method in "%s" is deprecated since Symfony 4.1.', ParentNodeDefinitionInterface::class, static::class), E_USER_DEPRECATED);
+ foreach ($this->getChildNodeDefinitions() as $child) {
+ $child->setPathSeparator($separator);
}
}
diff --git a/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php
index d3cdca90df1c3..06cbbd4345fff 100644
--- a/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php
+++ b/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php
@@ -30,12 +30,12 @@ public function __construct(NodeDefinition $node)
/**
* Registers a key to remap to its plural form.
*
- * @param string $key The key to remap
- * @param string $plural The plural of the key in case of irregular plural
+ * @param string $key The key to remap
+ * @param string|null $plural The plural of the key in case of irregular plural
*
* @return $this
*/
- public function remap($key, $plural = null)
+ public function remap(string $key, string $plural = null)
{
$this->remappings[] = [$key, null === $plural ? $key.'s' : $plural];
diff --git a/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php b/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php
index c6328c29449b8..449b91afdbc46 100644
--- a/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php
+++ b/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php
@@ -15,8 +15,6 @@
* An interface that must be implemented by nodes which can have children.
*
* @author Victor Berchet
- *
- * @method NodeDefinition[] getChildNodeDefinitions() Gets the child node definitions - not implementing it is deprecated since Symfony 4.2
*/
interface ParentNodeDefinitionInterface extends BuilderAwareInterface
{
@@ -43,4 +41,11 @@ public function children();
* @return $this
*/
public function append(NodeDefinition $node);
+
+ /**
+ * Gets the child node definitions.
+ *
+ * @return NodeDefinition[]
+ */
+ public function getChildNodeDefinitions();
}
diff --git a/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php
index 83f2c5991e4de..13a18db3ae1c5 100644
--- a/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php
+++ b/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Config\Definition\Builder;
-use Symfony\Component\Config\Definition\Exception\TreeWithoutRootNodeException;
use Symfony\Component\Config\Definition\NodeInterface;
/**
@@ -24,35 +23,10 @@ class TreeBuilder implements NodeParentInterface
protected $tree;
protected $root;
- public function __construct(string $name = null, string $type = 'array', NodeBuilder $builder = null)
+ public function __construct(string $name, string $type = 'array', NodeBuilder $builder = null)
{
- if (null === $name) {
- @trigger_error('A tree builder without a root node is deprecated since Symfony 4.2 and will not be supported anymore in 5.0.', E_USER_DEPRECATED);
- } else {
- $builder = $builder ?: new NodeBuilder();
- $this->root = $builder->node($name, $type)->setParent($this);
- }
- }
-
- /**
- * Creates the root node.
- *
- * @param string $name The name of the root node
- * @param string $type The type of the root node
- *
- * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array')
- *
- * @throws \RuntimeException When the node type is not supported
- *
- * @deprecated since Symfony 4.3, pass the root name to the constructor instead
- */
- public function root($name, $type = 'array', NodeBuilder $builder = null)
- {
- @trigger_error(sprintf('The "%s()" method called for the "%s" configuration is deprecated since Symfony 4.3, pass the root name to the constructor instead.', __METHOD__, $name), E_USER_DEPRECATED);
-
$builder = $builder ?: new NodeBuilder();
-
- return $this->root = $builder->node($name, $type)->setParent($this);
+ $this->root = $builder->node($name, $type)->setParent($this);
}
/**
@@ -60,10 +34,6 @@ public function root($name, $type = 'array', NodeBuilder $builder = null)
*/
public function getRootNode(): NodeDefinition
{
- if (null === $this->root) {
- throw new \RuntimeException(sprintf('Calling %s() before creating the root node is not supported, migrate to the new constructor signature instead.', __METHOD__));
- }
-
return $this->root;
}
@@ -76,7 +46,6 @@ public function getRootNode(): NodeDefinition
*/
public function buildTree()
{
- $this->assertTreeHasRootNode();
if (null !== $this->tree) {
return $this->tree;
}
@@ -86,21 +55,9 @@ public function buildTree()
public function setPathSeparator(string $separator)
{
- $this->assertTreeHasRootNode();
-
// unset last built as changing path separator changes all nodes
$this->tree = null;
$this->root->setPathSeparator($separator);
}
-
- /**
- * @throws \RuntimeException if root node is not defined
- */
- private function assertTreeHasRootNode()
- {
- if (null === $this->root) {
- throw new TreeWithoutRootNodeException('The configuration tree has no root node.');
- }
- }
}
diff --git a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php
index da0a971213ee7..b7659a364109d 100644
--- a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php
+++ b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php
@@ -26,12 +26,12 @@ class XmlReferenceDumper
{
private $reference;
- public function dump(ConfigurationInterface $configuration, $namespace = null)
+ public function dump(ConfigurationInterface $configuration, string $namespace = null)
{
return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace);
}
- public function dumpNode(NodeInterface $node, $namespace = null)
+ public function dumpNode(NodeInterface $node, string $namespace = null)
{
$this->reference = '';
$this->writeNode($node, 0, true, $namespace);
diff --git a/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php
index c53481bd96a63..757963d42a9be 100644
--- a/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php
+++ b/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php
@@ -33,7 +33,7 @@ public function dump(ConfigurationInterface $configuration)
return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree());
}
- public function dumpAtPath(ConfigurationInterface $configuration, $path)
+ public function dumpAtPath(ConfigurationInterface $configuration, string $path)
{
$rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree();
diff --git a/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php b/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php
index 3dbc57b15317f..fcaaf49435f9d 100644
--- a/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php
+++ b/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php
@@ -34,10 +34,8 @@ public function getPath()
/**
* Adds extra information that is suffixed to the original exception message.
- *
- * @param string $hint
*/
- public function addHint($hint)
+ public function addHint(string $hint)
{
if (!$this->containsHints) {
$this->message .= "\nHint: ".$hint;
diff --git a/src/Symfony/Component/Config/Definition/Exception/TreeWithoutRootNodeException.php b/src/Symfony/Component/Config/Definition/Exception/TreeWithoutRootNodeException.php
deleted file mode 100644
index 04406fc90b416..0000000000000
--- a/src/Symfony/Component/Config/Definition/Exception/TreeWithoutRootNodeException.php
+++ /dev/null
@@ -1,21 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Config\Definition\Exception;
-
-/**
- * @author Roland Franssen
- *
- * @internal
- */
-class TreeWithoutRootNodeException extends \RuntimeException
-{
-}
diff --git a/src/Symfony/Component/Config/Definition/Processor.php b/src/Symfony/Component/Config/Definition/Processor.php
index a878b90557a75..76f470047305a 100644
--- a/src/Symfony/Component/Config/Definition/Processor.php
+++ b/src/Symfony/Component/Config/Definition/Processor.php
@@ -16,7 +16,7 @@
*
* @author Johannes M. Schmitt
*
- * @final since version 4.1
+ * @final
*/
class Processor
{
@@ -27,7 +27,7 @@ class Processor
*
* @return array The processed configuration
*/
- public function process(NodeInterface $configTree, array $configs)
+ public function process(NodeInterface $configTree, array $configs): array
{
$currentConfig = [];
foreach ($configs as $config) {
@@ -45,7 +45,7 @@ public function process(NodeInterface $configTree, array $configs)
*
* @return array The processed configuration
*/
- public function processConfiguration(ConfigurationInterface $configuration, array $configs)
+ public function processConfiguration(ConfigurationInterface $configuration, array $configs): array
{
return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs);
}
@@ -70,10 +70,8 @@ public function processConfiguration(ConfigurationInterface $configuration, arra
* @param array $config A config array
* @param string $key The key to normalize
* @param string $plural The plural form of the key if it is irregular
- *
- * @return array
*/
- public static function normalizeConfig($config, $key, $plural = null)
+ public static function normalizeConfig(array $config, string $key, string $plural = null): array
{
if (null === $plural) {
$plural = $key.'s';
diff --git a/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php b/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php
index 8bbb84d4dcc7b..b160aa94a4963 100644
--- a/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php
+++ b/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php
@@ -20,8 +20,6 @@ interface PrototypeNodeInterface extends NodeInterface
{
/**
* Sets the name of the node.
- *
- * @param string $name The name of the node
*/
- public function setName($name);
+ public function setName(string $name);
}
diff --git a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
index 72d8fa0072dc0..ff4570ee765e0 100644
--- a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
@@ -37,10 +37,8 @@ class PrototypedArrayNode extends ArrayNode
/**
* Sets the minimum number of elements that a prototype based node must
* contain. By default this is zero, meaning no elements.
- *
- * @param int $number
*/
- public function setMinNumberOfElements($number)
+ public function setMinNumberOfElements(int $number)
{
$this->minNumberOfElements = $number;
}
@@ -69,7 +67,7 @@ public function setMinNumberOfElements($number)
* @param string $attribute The name of the attribute which value is to be used as a key
* @param bool $remove Whether or not to remove the key
*/
- public function setKeyAttribute($attribute, $remove = true)
+ public function setKeyAttribute(string $attribute, bool $remove = true)
{
$this->keyAttribute = $attribute;
$this->removeKeyAttribute = $remove;
@@ -87,17 +85,9 @@ public function getKeyAttribute()
/**
* Sets the default value of this node.
- *
- * @param string $value
- *
- * @throws \InvalidArgumentException if the default value is not an array
*/
- public function setDefaultValue($value)
+ public function setDefaultValue(array $value)
{
- if (!\is_array($value)) {
- throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.');
- }
-
$this->defaultValue = $value;
}
diff --git a/src/Symfony/Component/Config/Definition/VariableNode.php b/src/Symfony/Component/Config/Definition/VariableNode.php
index ad70d6e0e08cb..e868ece13e543 100644
--- a/src/Symfony/Component/Config/Definition/VariableNode.php
+++ b/src/Symfony/Component/Config/Definition/VariableNode.php
@@ -56,15 +56,15 @@ public function getDefaultValue()
*
* @param bool $boolean True if this entity will accept empty values
*/
- public function setAllowEmptyValue($boolean)
+ public function setAllowEmptyValue(bool $boolean)
{
- $this->allowEmptyValue = (bool) $boolean;
+ $this->allowEmptyValue = $boolean;
}
/**
* {@inheritdoc}
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -84,14 +84,13 @@ protected function finalizeValue($value)
// deny environment variables only when using custom validators
// this avoids ever passing an empty value to final validation closures
if (!$this->allowEmptyValue && $this->isHandlingPlaceholder() && $this->finalValidationClosures) {
- @trigger_error(sprintf('Setting path "%s" to an environment variable is deprecated since Symfony 4.3. Remove "cannotBeEmpty()", "validate()" or include a prefix/suffix value instead.', $this->getPath()), E_USER_DEPRECATED);
-// $e = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an environment variable when empty values are not allowed by definition and are validated.', $this->getPath()));
-// if ($hint = $this->getInfo()) {
-// $e->addHint($hint);
-// }
-// $e->setPath($this->getPath());
-//
-// throw $e;
+ $e = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an environment variable when empty values are not allowed by definition and are validated.', $this->getPath()));
+ if ($hint = $this->getInfo()) {
+ $e->addHint($hint);
+ }
+ $e->setPath($this->getPath());
+
+ throw $e;
}
if (!$this->allowEmptyValue && $this->isValueEmpty($value)) {
diff --git a/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php b/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php
deleted file mode 100644
index 16beec5930c82..0000000000000
--- a/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php
+++ /dev/null
@@ -1,111 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Config\Exception;
-
-/**
- * Exception class for when a resource cannot be loaded or imported.
- *
- * @author Ryan Weaver
- *
- * @deprecated since Symfony 4.2, use LoaderLoadException instead.
- */
-class FileLoaderLoadException extends \Exception
-{
- /**
- * @param string $resource The resource that could not be imported
- * @param string $sourceResource The original resource importing the new resource
- * @param int $code The error code
- * @param \Throwable $previous A previous exception
- * @param string $type The type of resource
- */
- public function __construct(string $resource, string $sourceResource = null, int $code = null, \Throwable $previous = null, string $type = null)
- {
- $message = '';
- if ($previous) {
- // Include the previous exception, to help the user see what might be the underlying cause
-
- // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim...
- if ('.' === substr($previous->getMessage(), -1)) {
- $trimmedMessage = substr($previous->getMessage(), 0, -1);
- $message .= sprintf('%s', $trimmedMessage).' in ';
- } else {
- $message .= sprintf('%s', $previous->getMessage()).' in ';
- }
- $message .= $resource.' ';
-
- // show tweaked trace to complete the human readable sentence
- if (null === $sourceResource) {
- $message .= sprintf('(which is loaded in resource "%s")', $resource);
- } else {
- $message .= sprintf('(which is being imported from "%s")', $sourceResource);
- }
- $message .= '.';
-
- // if there's no previous message, present it the default way
- } elseif (null === $sourceResource) {
- $message .= sprintf('Cannot load resource "%s".', $resource);
- } else {
- $message .= sprintf('Cannot import resource "%s" from "%s".', $resource, $sourceResource);
- }
-
- // Is the resource located inside a bundle?
- if ('@' === $resource[0]) {
- $parts = explode(\DIRECTORY_SEPARATOR, $resource);
- $bundle = substr($parts[0], 1);
- $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle);
- $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource);
- } elseif (null !== $type) {
- // maybe there is no loader for this specific type
- if ('annotation' === $type) {
- $message .= ' Make sure annotations are installed and enabled.';
- } else {
- $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type);
- }
- }
-
- parent::__construct($message, $code, $previous);
- }
-
- protected function varToString($var)
- {
- if (\is_object($var)) {
- return sprintf('Object(%s)', \get_class($var));
- }
-
- if (\is_array($var)) {
- $a = [];
- foreach ($var as $k => $v) {
- $a[] = sprintf('%s => %s', $k, $this->varToString($v));
- }
-
- return sprintf('Array(%s)', implode(', ', $a));
- }
-
- if (\is_resource($var)) {
- return sprintf('Resource(%s)', get_resource_type($var));
- }
-
- if (null === $var) {
- return 'null';
- }
-
- if (false === $var) {
- return 'false';
- }
-
- if (true === $var) {
- return 'true';
- }
-
- return (string) $var;
- }
-}
diff --git a/src/Symfony/Component/Config/Exception/LoaderLoadException.php b/src/Symfony/Component/Config/Exception/LoaderLoadException.php
index 41a959d38df8f..dcec3201bdb20 100644
--- a/src/Symfony/Component/Config/Exception/LoaderLoadException.php
+++ b/src/Symfony/Component/Config/Exception/LoaderLoadException.php
@@ -16,6 +16,94 @@
*
* @author Ryan Weaver
*/
-class LoaderLoadException extends FileLoaderLoadException
+class LoaderLoadException extends \Exception
{
+ /**
+ * @param string $resource The resource that could not be imported
+ * @param string $sourceResource The original resource importing the new resource
+ * @param int $code The error code
+ * @param \Throwable $previous A previous exception
+ * @param string $type The type of resource
+ */
+ public function __construct(string $resource, string $sourceResource = null, int $code = null, \Throwable $previous = null, string $type = null)
+ {
+ $message = '';
+ if ($previous) {
+ // Include the previous exception, to help the user see what might be the underlying cause
+
+ // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim...
+ if ('.' === substr($previous->getMessage(), -1)) {
+ $trimmedMessage = substr($previous->getMessage(), 0, -1);
+ $message .= sprintf('%s', $trimmedMessage).' in ';
+ } else {
+ $message .= sprintf('%s', $previous->getMessage()).' in ';
+ }
+ $message .= $resource.' ';
+
+ // show tweaked trace to complete the human readable sentence
+ if (null === $sourceResource) {
+ $message .= sprintf('(which is loaded in resource "%s")', $resource);
+ } else {
+ $message .= sprintf('(which is being imported from "%s")', $sourceResource);
+ }
+ $message .= '.';
+
+ // if there's no previous message, present it the default way
+ } elseif (null === $sourceResource) {
+ $message .= sprintf('Cannot load resource "%s".', $resource);
+ } else {
+ $message .= sprintf('Cannot import resource "%s" from "%s".', $resource, $sourceResource);
+ }
+
+ // Is the resource located inside a bundle?
+ if ('@' === $resource[0]) {
+ $parts = explode(\DIRECTORY_SEPARATOR, $resource);
+ $bundle = substr($parts[0], 1);
+ $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle);
+ $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource);
+ } elseif (null !== $type) {
+ // maybe there is no loader for this specific type
+ if ('annotation' === $type) {
+ $message .= ' Make sure annotations are installed and enabled.';
+ } else {
+ $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type);
+ }
+ }
+
+ parent::__construct($message, $code, $previous);
+ }
+
+ protected function varToString($var)
+ {
+ if (\is_object($var)) {
+ return sprintf('Object(%s)', \get_class($var));
+ }
+
+ if (\is_array($var)) {
+ $a = [];
+ foreach ($var as $k => $v) {
+ $a[] = sprintf('%s => %s', $k, $this->varToString($v));
+ }
+
+ return sprintf('Array(%s)', implode(', ', $a));
+ }
+
+ if (\is_resource($var)) {
+ return sprintf('Resource(%s)', get_resource_type($var));
+ }
+
+ if (null === $var) {
+ return 'null';
+ }
+
+ if (false === $var) {
+ return 'false';
+ }
+
+ if (true === $var) {
+ return 'true';
+ }
+
+ return (string) $var;
+ }
}
diff --git a/src/Symfony/Component/Config/FileLocator.php b/src/Symfony/Component/Config/FileLocator.php
index ce856a868ff49..965f940d45dcc 100644
--- a/src/Symfony/Component/Config/FileLocator.php
+++ b/src/Symfony/Component/Config/FileLocator.php
@@ -33,9 +33,9 @@ public function __construct($paths = [])
/**
* {@inheritdoc}
*/
- public function locate($name, $currentPath = null, $first = true)
+ public function locate(string $name, string $currentPath = null, bool $first = true)
{
- if ('' == $name) {
+ if ('' === $name) {
throw new \InvalidArgumentException('An empty file name is not valid to be located.');
}
diff --git a/src/Symfony/Component/Config/FileLocatorInterface.php b/src/Symfony/Component/Config/FileLocatorInterface.php
index cf3c2e594df85..e3ca1d49c4066 100644
--- a/src/Symfony/Component/Config/FileLocatorInterface.php
+++ b/src/Symfony/Component/Config/FileLocatorInterface.php
@@ -30,5 +30,5 @@ interface FileLocatorInterface
* @throws \InvalidArgumentException If $name is empty
* @throws FileLocatorFileNotFoundException If a file is not found
*/
- public function locate($name, $currentPath = null, $first = true);
+ public function locate(string $name, string $currentPath = null, bool $first = true);
}
diff --git a/src/Symfony/Component/Config/Loader/DelegatingLoader.php b/src/Symfony/Component/Config/Loader/DelegatingLoader.php
index e40e57d7af6aa..2ec64cb021246 100644
--- a/src/Symfony/Component/Config/Loader/DelegatingLoader.php
+++ b/src/Symfony/Component/Config/Loader/DelegatingLoader.php
@@ -31,7 +31,7 @@ public function __construct(LoaderResolverInterface $resolver)
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
if (false === $loader = $this->resolver->resolve($resource, $type)) {
throw new LoaderLoadException($resource, null, null, null, $type);
@@ -43,7 +43,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
return false !== $this->resolver->resolve($resource, $type);
}
diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php
index 5055e1747e7e0..58dc4e7f89d6e 100644
--- a/src/Symfony/Component/Config/Loader/FileLoader.php
+++ b/src/Symfony/Component/Config/Loader/FileLoader.php
@@ -38,10 +38,8 @@ public function __construct(FileLocatorInterface $locator)
/**
* Sets the current directory.
- *
- * @param string $dir
*/
- public function setCurrentDir($dir)
+ public function setCurrentDir(string $dir)
{
$this->currentDir = $dir;
}
@@ -71,13 +69,8 @@ public function getLocator()
* @throws FileLoaderImportCircularReferenceException
* @throws FileLocatorFileNotFoundException
*/
- public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null/*, $exclude = null*/)
+ public function import($resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null, $exclude = null)
{
- if (\func_num_args() < 5 && __CLASS__ !== static::class && 0 !== strpos(static::class, 'Symfony\Component\\') && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
- @trigger_error(sprintf('The "%s()" method will have a new "$exclude = null" argument in version 5.0, not defining it is deprecated since Symfony 4.4.', __METHOD__), E_USER_DEPRECATED);
- }
- $exclude = \func_num_args() >= 5 ? func_get_arg(4) : null;
-
if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) {
$excluded = [];
foreach ((array) $exclude as $pattern) {
@@ -139,7 +132,7 @@ protected function glob(string $pattern, bool $recursive, &$resource = null, boo
yield from $resource;
}
- private function doImport($resource, string $type = null, bool $ignoreErrors = false, $sourceResource = null)
+ private function doImport($resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null)
{
try {
$loader = $this->resolve($resource, $type);
diff --git a/src/Symfony/Component/Config/Loader/GlobFileLoader.php b/src/Symfony/Component/Config/Loader/GlobFileLoader.php
index f432b45ba4fe8..fecb1c5d073ac 100644
--- a/src/Symfony/Component/Config/Loader/GlobFileLoader.php
+++ b/src/Symfony/Component/Config/Loader/GlobFileLoader.php
@@ -21,7 +21,7 @@ class GlobFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
return $this->import($resource);
}
@@ -29,7 +29,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
return 'glob' === $type;
}
diff --git a/src/Symfony/Component/Config/Loader/Loader.php b/src/Symfony/Component/Config/Loader/Loader.php
index 698ab40afbf3d..62cae685e2981 100644
--- a/src/Symfony/Component/Config/Loader/Loader.php
+++ b/src/Symfony/Component/Config/Loader/Loader.php
@@ -46,7 +46,7 @@ public function setResolver(LoaderResolverInterface $resolver)
*
* @return mixed
*/
- public function import($resource, $type = null)
+ public function import($resource, string $type = null)
{
return $this->resolve($resource, $type)->load($resource, $type);
}
@@ -61,7 +61,7 @@ public function import($resource, $type = null)
*
* @throws LoaderLoadException If no loader is found
*/
- public function resolve($resource, $type = null)
+ public function resolve($resource, string $type = null)
{
if ($this->supports($resource, $type)) {
return $this;
diff --git a/src/Symfony/Component/Config/Loader/LoaderInterface.php b/src/Symfony/Component/Config/Loader/LoaderInterface.php
index dfca9dd27bf0d..4dfb42c53fada 100644
--- a/src/Symfony/Component/Config/Loader/LoaderInterface.php
+++ b/src/Symfony/Component/Config/Loader/LoaderInterface.php
@@ -21,22 +21,20 @@ interface LoaderInterface
/**
* Loads a resource.
*
- * @param mixed $resource The resource
- * @param string|null $type The resource type or null if unknown
+ * @param mixed $resource The resource
*
* @throws \Exception If something went wrong
*/
- public function load($resource, $type = null);
+ public function load($resource, string $type = null);
/**
* Returns whether this class supports the given resource.
*
- * @param mixed $resource A resource
- * @param string|null $type The resource type or null if unknown
+ * @param mixed $resource A resource
*
* @return bool True if this class supports the given resource, false otherwise
*/
- public function supports($resource, $type = null);
+ public function supports($resource, string $type = null);
/**
* Gets the loader resolver.
diff --git a/src/Symfony/Component/Config/Loader/LoaderResolver.php b/src/Symfony/Component/Config/Loader/LoaderResolver.php
index c99efda4fd365..d243b91d42542 100644
--- a/src/Symfony/Component/Config/Loader/LoaderResolver.php
+++ b/src/Symfony/Component/Config/Loader/LoaderResolver.php
@@ -39,7 +39,7 @@ public function __construct(array $loaders = [])
/**
* {@inheritdoc}
*/
- public function resolve($resource, $type = null)
+ public function resolve($resource, string $type = null)
{
foreach ($this->loaders as $loader) {
if ($loader->supports($resource, $type)) {
diff --git a/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php b/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php
index 40f1a1a63f295..2c45a4cbd79b7 100644
--- a/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php
+++ b/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php
@@ -26,5 +26,5 @@ interface LoaderResolverInterface
*
* @return LoaderInterface|false The loader or false if none is able to load the resource
*/
- public function resolve($resource, $type = null);
+ public function resolve($resource, string $type = null);
}
diff --git a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php
index 144e1842f934b..cce387ccfe3c2 100644
--- a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php
+++ b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php
@@ -19,7 +19,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class ClassExistenceResource implements SelfCheckingResourceInterface
{
@@ -45,7 +45,7 @@ public function __construct(string $resource, bool $exists = null)
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return $this->resource;
}
@@ -53,7 +53,7 @@ public function __toString()
/**
* @return string The file path to the resource
*/
- public function getResource()
+ public function getResource(): string
{
return $this->resource;
}
@@ -63,7 +63,7 @@ public function getResource()
*
* @throws \ReflectionException when a parent class/interface/trait is not found
*/
- public function isFresh($timestamp)
+ public function isFresh(int $timestamp): bool
{
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
@@ -149,7 +149,7 @@ public function __wakeup()
*
* @internal
*/
- public static function throwOnRequiredClass($class, \Exception $previous = null)
+ public static function throwOnRequiredClass(string $class, \Exception $previous = null)
{
// If the passed class is the resource being checked, we shouldn't throw.
if (null === $previous && self::$autoloadedClass === $class) {
diff --git a/src/Symfony/Component/Config/Resource/ComposerResource.php b/src/Symfony/Component/Config/Resource/ComposerResource.php
index 822766b75b1cb..e2abe0cb72328 100644
--- a/src/Symfony/Component/Config/Resource/ComposerResource.php
+++ b/src/Symfony/Component/Config/Resource/ComposerResource.php
@@ -16,7 +16,7 @@
*
* @author Nicolas Grekas
*
- * @final since Symfony 4.3
+ * @final
*/
class ComposerResource implements SelfCheckingResourceInterface
{
@@ -30,7 +30,7 @@ public function __construct()
$this->vendors = self::$runtimeVendors;
}
- public function getVendors()
+ public function getVendors(): array
{
return array_keys($this->vendors);
}
@@ -38,7 +38,7 @@ public function getVendors()
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return __CLASS__;
}
@@ -46,7 +46,7 @@ public function __toString()
/**
* {@inheritdoc}
*/
- public function isFresh($timestamp)
+ public function isFresh(int $timestamp): bool
{
self::refresh();
diff --git a/src/Symfony/Component/Config/Resource/DirectoryResource.php b/src/Symfony/Component/Config/Resource/DirectoryResource.php
index 3d703db7f6ebe..1a28881f3fec1 100644
--- a/src/Symfony/Component/Config/Resource/DirectoryResource.php
+++ b/src/Symfony/Component/Config/Resource/DirectoryResource.php
@@ -16,7 +16,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class DirectoryResource implements SelfCheckingResourceInterface
{
@@ -42,7 +42,7 @@ public function __construct(string $resource, string $pattern = null)
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return md5(serialize([$this->resource, $this->pattern]));
}
@@ -50,17 +50,15 @@ public function __toString()
/**
* @return string The file path to the resource
*/
- public function getResource()
+ public function getResource(): string
{
return $this->resource;
}
/**
* Returns the pattern to restrict monitored files.
- *
- * @return string|null
*/
- public function getPattern()
+ public function getPattern(): ?string
{
return $this->pattern;
}
@@ -68,7 +66,7 @@ public function getPattern()
/**
* {@inheritdoc}
*/
- public function isFresh($timestamp)
+ public function isFresh(int $timestamp): bool
{
if (!is_dir($this->resource)) {
return false;
diff --git a/src/Symfony/Component/Config/Resource/FileExistenceResource.php b/src/Symfony/Component/Config/Resource/FileExistenceResource.php
index 57234161588c7..ca2d2c3f79816 100644
--- a/src/Symfony/Component/Config/Resource/FileExistenceResource.php
+++ b/src/Symfony/Component/Config/Resource/FileExistenceResource.php
@@ -19,7 +19,7 @@
*
* @author Charles-Henri Bruyand
*
- * @final since Symfony 4.3
+ * @final
*/
class FileExistenceResource implements SelfCheckingResourceInterface
{
@@ -39,7 +39,7 @@ public function __construct(string $resource)
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return $this->resource;
}
@@ -47,7 +47,7 @@ public function __toString()
/**
* @return string The file path to the resource
*/
- public function getResource()
+ public function getResource(): string
{
return $this->resource;
}
@@ -55,7 +55,7 @@ public function getResource()
/**
* {@inheritdoc}
*/
- public function isFresh($timestamp)
+ public function isFresh(int $timestamp): bool
{
return file_exists($this->resource) === $this->exists;
}
diff --git a/src/Symfony/Component/Config/Resource/FileResource.php b/src/Symfony/Component/Config/Resource/FileResource.php
index 95fe8a0bf802c..4274d07bacb4d 100644
--- a/src/Symfony/Component/Config/Resource/FileResource.php
+++ b/src/Symfony/Component/Config/Resource/FileResource.php
@@ -18,7 +18,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class FileResource implements SelfCheckingResourceInterface
{
@@ -44,7 +44,7 @@ public function __construct(string $resource)
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return $this->resource;
}
@@ -52,7 +52,7 @@ public function __toString()
/**
* @return string The canonicalized, absolute path to the resource
*/
- public function getResource()
+ public function getResource(): string
{
return $this->resource;
}
@@ -60,7 +60,7 @@ public function getResource()
/**
* {@inheritdoc}
*/
- public function isFresh($timestamp)
+ public function isFresh(int $timestamp): bool
{
return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp;
}
diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php
index 9f6e0d8dcf88d..8f5a2f1799051 100644
--- a/src/Symfony/Component/Config/Resource/GlobResource.php
+++ b/src/Symfony/Component/Config/Resource/GlobResource.php
@@ -21,7 +21,7 @@
*
* @author Nicolas Grekas
*
- * @final since Symfony 4.3
+ * @final
*/
class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface
{
@@ -55,7 +55,7 @@ public function __construct(string $prefix, string $pattern, bool $recursive, bo
}
}
- public function getPrefix()
+ public function getPrefix(): string
{
return $this->prefix;
}
@@ -63,7 +63,7 @@ public function getPrefix()
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return 'glob.'.$this->prefix.(int) $this->recursive.$this->pattern.(int) $this->forExclusion.implode("\0", $this->excludedPrefixes);
}
@@ -71,7 +71,7 @@ public function __toString()
/**
* {@inheritdoc}
*/
- public function isFresh($timestamp)
+ public function isFresh(int $timestamp): bool
{
$hash = $this->computeHash();
@@ -94,10 +94,7 @@ public function __sleep(): array
return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes'];
}
- /**
- * @return \Traversable
- */
- public function getIterator()
+ public function getIterator(): \Traversable
{
if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
return;
diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php
index 48fdcd69b056c..bca92690db899 100644
--- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php
+++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Config\Resource;
-use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
@@ -19,7 +18,7 @@
/**
* @author Nicolas Grekas
*
- * @final since Symfony 4.3
+ * @final
*/
class ReflectionClassResource implements SelfCheckingResourceInterface
{
@@ -36,7 +35,10 @@ public function __construct(\ReflectionClass $classReflector, array $excludedVen
$this->excludedVendors = $excludedVendors;
}
- public function isFresh($timestamp)
+ /**
+ * {@inheritdoc}
+ */
+ public function isFresh(int $timestamp): bool
{
if (null === $this->hash) {
$this->hash = $this->computeHash();
@@ -56,7 +58,7 @@ public function isFresh($timestamp)
return true;
}
- public function __toString()
+ public function __toString(): string
{
return 'reflection.'.$this->className;
}
@@ -210,10 +212,7 @@ private function generateSignature(\ReflectionClass $class): iterable
}
}
- if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) {
- yield LegacyServiceSubscriberInterface::class;
- yield print_r([$class->name, 'getSubscribedServices'](), true);
- } elseif (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
+ if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
yield ServiceSubscriberInterface::class;
yield print_r($class->name::getSubscribedServices(), true);
}
diff --git a/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php b/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php
index d72203bc1a42c..eab332393ab28 100644
--- a/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php
+++ b/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php
@@ -28,7 +28,7 @@ public function supports(ResourceInterface $metadata)
return $metadata instanceof SelfCheckingResourceInterface;
}
- public function isFresh(ResourceInterface $resource, $timestamp)
+ public function isFresh(ResourceInterface $resource, int $timestamp)
{
/* @var SelfCheckingResourceInterface $resource */
return $resource->isFresh($timestamp);
diff --git a/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php b/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php
index b3260f2be3e58..c08d96973ec95 100644
--- a/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php
+++ b/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php
@@ -26,5 +26,5 @@ interface SelfCheckingResourceInterface extends ResourceInterface
*
* @return bool True if the resource has not been updated, false otherwise
*/
- public function isFresh($timestamp);
+ public function isFresh(int $timestamp);
}
diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php
index ac4adb7f339f6..753ee4dcaf608 100644
--- a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php
+++ b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php
@@ -116,7 +116,7 @@ public function isFresh()
*
* @throws \RuntimeException When cache file can't be written
*/
- public function write($content, array $metadata = null)
+ public function write(string $content, array $metadata = null)
{
$mode = 0666;
$umask = umask();
diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php b/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php
index 0338635ff5a62..a789644cae13f 100644
--- a/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php
+++ b/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php
@@ -32,15 +32,11 @@ public function __construct(iterable $resourceCheckers = [])
/**
* {@inheritdoc}
*/
- public function cache($file, $callback)
+ public function cache(string $file, callable $callable)
{
- if (!\is_callable($callback)) {
- throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback)));
- }
-
$cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers);
if (!$cache->isFresh()) {
- $callback($cache);
+ $callable($cache);
}
return $cache;
diff --git a/src/Symfony/Component/Config/ResourceCheckerInterface.php b/src/Symfony/Component/Config/ResourceCheckerInterface.php
index ac0d402495e7f..0688f51e980bc 100644
--- a/src/Symfony/Component/Config/ResourceCheckerInterface.php
+++ b/src/Symfony/Component/Config/ResourceCheckerInterface.php
@@ -41,5 +41,5 @@ public function supports(ResourceInterface $metadata);
*
* @return bool True if the resource has not changed since the given timestamp, false otherwise
*/
- public function isFresh(ResourceInterface $resource, $timestamp);
+ public function isFresh(ResourceInterface $resource, int $timestamp);
}
diff --git a/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php b/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php
index 6190b9b450b40..d61582b652936 100644
--- a/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php
+++ b/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php
@@ -18,8 +18,7 @@ class ConfigCacheFactoryTest extends TestCase
{
public function testCacheWithInvalidCallback()
{
- $this->expectException('InvalidArgumentException');
- $this->expectExceptionMessage('Invalid type for callback argument. Expected callable, but got "object".');
+ $this->expectException('TypeError');
$cacheFactory = new ConfigCacheFactory(true);
$cacheFactory->cache('file', new \stdClass());
diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php
index baeb8a4b16c09..ab21226ae2caf 100644
--- a/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php
+++ b/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php
@@ -188,23 +188,4 @@ public function testPathSeparatorIsPropagatedToChildren()
$this->assertInstanceOf('Symfony\Component\Config\Definition\BaseNode', $childChildren['foo']);
$this->assertSame('propagation/child/foo', $childChildren['foo']->getPath());
}
-
- /**
- * @group legacy
- * @expectedDeprecation A tree builder without a root node is deprecated since Symfony 4.2 and will not be supported anymore in 5.0.
- */
- public function testInitializingTreeBuildersWithoutRootNode()
- {
- new TreeBuilder();
- }
-
- /**
- * @group legacy
- * @expectedDeprecation The "Symfony\Component\Config\Definition\Builder\TreeBuilder::root()" method called for the "foo" configuration is deprecated since Symfony 4.3, pass the root name to the constructor instead.
- */
- public function testRoot()
- {
- $builder = new TreeBuilder('foo');
- $builder->root('foo');
- }
}
diff --git a/src/Symfony/Component/Config/Tests/FileLocatorTest.php b/src/Symfony/Component/Config/Tests/FileLocatorTest.php
index e931916af9187..d4a990b5bef7b 100644
--- a/src/Symfony/Component/Config/Tests/FileLocatorTest.php
+++ b/src/Symfony/Component/Config/Tests/FileLocatorTest.php
@@ -109,6 +109,6 @@ public function testLocateEmpty()
$this->expectExceptionMessage('An empty file name is not valid to be located.');
$loader = new FileLocator([__DIR__.'/Fixtures']);
- $loader->locate(null, __DIR__);
+ $loader->locate('', __DIR__);
}
}
diff --git a/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php
index 26323e9805d64..b8000bbdd895f 100644
--- a/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php
+++ b/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php
@@ -20,7 +20,7 @@ public function barNode($name)
return $this->node($name, 'bar');
}
- protected function getNodeClass($type): string
+ protected function getNodeClass(string $type): string
{
switch ($type) {
case 'variable':
diff --git a/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php
index 6cf625e6c730f..f6c16c631254a 100644
--- a/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php
+++ b/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php
@@ -106,12 +106,12 @@ class TestFileLoader extends FileLoader
{
private $supports = true;
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
return $resource;
}
- public function supports($resource, $type = null): bool
+ public function supports($resource, string $type = null): bool
{
return $this->supports;
}
diff --git a/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php b/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php
index 6c582ca8d5755..e1660da6e38d0 100644
--- a/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php
+++ b/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php
@@ -101,11 +101,11 @@ public function testImportWithType()
class ProjectLoader1 extends Loader
{
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
}
- public function supports($resource, $type = null): bool
+ public function supports($resource, string $type = null): bool
{
return \is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION);
}
diff --git a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php
index 413a495825e1d..629cd0b3ff6d4 100644
--- a/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php
+++ b/src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php
@@ -117,7 +117,7 @@ public function testHashedSignature($changeExpected, $changedLine, $changedCode,
}
}
- public function provideHashedSignature()
+ public function provideHashedSignature(): iterable
{
yield [0, 0, "// line change\n\n"];
yield [1, 0, '/** class docblock */'];
diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php
index 6688d93e31a27..0ebeec4daaf57 100644
--- a/src/Symfony/Component/Config/Util/XmlUtils.php
+++ b/src/Symfony/Component/Config/Util/XmlUtils.php
@@ -44,7 +44,7 @@ private function __construct()
* @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself
* @throws \RuntimeException When DOM extension is missing
*/
- public static function parse($content, $schemaOrCallable = null)
+ public static function parse(string $content, $schemaOrCallable = null)
{
if (!\extension_loaded('dom')) {
throw new \LogicException('Extension DOM is required.');
@@ -120,7 +120,7 @@ public static function parse($content, $schemaOrCallable = null)
* @throws XmlParsingException When XML parsing returns any errors
* @throws \RuntimeException When DOM extension is missing
*/
- public static function loadFile($file, $schemaOrCallable = null)
+ public static function loadFile(string $file, $schemaOrCallable = null)
{
$content = @file_get_contents($file);
if ('' === trim($content)) {
@@ -154,7 +154,7 @@ public static function loadFile($file, $schemaOrCallable = null)
*
* @return mixed
*/
- public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true)
+ public static function convertDomElementToArray(\DOMElement $element, bool $checkPrefix = true)
{
$prefix = (string) $element->prefix;
$empty = true;
@@ -247,7 +247,7 @@ public static function phpize($value)
}
}
- protected static function getXmlErrors($internalErrors)
+ protected static function getXmlErrors(bool $internalErrors)
{
$errors = [];
foreach (libxml_get_errors() as $error) {
diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json
index 78433bbe4d293..78db0bb31d282 100644
--- a/src/Symfony/Component/Config/composer.json
+++ b/src/Symfony/Component/Config/composer.json
@@ -16,19 +16,20 @@
}
],
"require": {
- "php": "^7.1.3",
- "symfony/filesystem": "^3.4|^4.0|^5.0",
+ "php": "^7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
+ "symfony/filesystem": "^4.4|^5.0",
"symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
- "symfony/event-dispatcher": "^3.4|^4.0|^5.0",
- "symfony/finder": "^3.4|^4.0|^5.0",
- "symfony/messenger": "^4.1|^5.0",
+ "symfony/event-dispatcher": "^4.4|^5.0",
+ "symfony/finder": "^4.4|^5.0",
+ "symfony/messenger": "^4.4|^5.0",
"symfony/service-contracts": "^1.1|^2",
- "symfony/yaml": "^3.4|^4.0|^5.0"
+ "symfony/yaml": "^4.4|^5.0"
},
"conflict": {
- "symfony/finder": "<3.4"
+ "symfony/finder": "<4.4"
},
"suggest": {
"symfony/yaml": "To use the yaml reference dumper"
@@ -42,7 +43,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index acd32687880c5..3de7a04d521b5 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -40,11 +40,8 @@
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
-use Symfony\Component\Debug\ErrorHandler as LegacyErrorHandler;
-use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\ErrorHandler\ErrorHandler;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Service\ResetInterface;
/**
@@ -80,10 +77,6 @@ class Application implements ResetInterface
private $singleCommand = false;
private $initialized;
- /**
- * @param string $name The name of the application
- * @param string $version The version of the application
- */
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
{
$this->name = $name;
@@ -93,11 +86,11 @@ public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN
}
/**
- * @final since Symfony 4.3, the type-hint will be updated to the interface from symfony/contracts in 5.0
+ * @final
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
- $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
+ $this->dispatcher = $dispatcher;
}
public function setCommandLoader(CommandLoaderInterface $commandLoader)
@@ -134,7 +127,7 @@ public function run(InputInterface $input = null, OutputInterface $output = null
};
if ($phpHandler = set_exception_handler($renderException)) {
restore_exception_handler();
- if (!\is_array($phpHandler) || (!$phpHandler[0] instanceof ErrorHandler && !$phpHandler[0] instanceof LegacyErrorHandler)) {
+ if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
$errorHandler = true;
} elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
$phpHandler[0]->setExceptionHandler($errorHandler);
@@ -348,12 +341,10 @@ public function areExceptionsCaught()
/**
* Sets whether to catch exceptions or not during commands execution.
- *
- * @param bool $boolean Whether to catch exceptions or not during commands execution
*/
- public function setCatchExceptions($boolean)
+ public function setCatchExceptions(bool $boolean)
{
- $this->catchExceptions = (bool) $boolean;
+ $this->catchExceptions = $boolean;
}
/**
@@ -368,12 +359,10 @@ public function isAutoExitEnabled()
/**
* Sets whether to automatically exit after a command execution or not.
- *
- * @param bool $boolean Whether to automatically exit after a command execution or not
*/
- public function setAutoExit($boolean)
+ public function setAutoExit(bool $boolean)
{
- $this->autoExit = (bool) $boolean;
+ $this->autoExit = $boolean;
}
/**
@@ -388,10 +377,8 @@ public function getName()
/**
* Sets the application name.
- *
- * @param string $name The application name
- */
- public function setName($name)
+ **/
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -408,10 +395,8 @@ public function getVersion()
/**
* Sets the application version.
- *
- * @param string $version The application version
*/
- public function setVersion($version)
+ public function setVersion(string $version)
{
$this->version = $version;
}
@@ -437,11 +422,9 @@ public function getLongVersion()
/**
* Registers a new command.
*
- * @param string $name The command name
- *
* @return Command The newly created command
*/
- public function register($name)
+ public function register(string $name)
{
return $this->add(new Command($name));
}
@@ -499,13 +482,11 @@ public function add(Command $command)
/**
* Returns a registered command by name or alias.
*
- * @param string $name The command name or alias
- *
* @return Command A Command object
*
* @throws CommandNotFoundException When given command name does not exist
*/
- public function get($name)
+ public function get(string $name)
{
$this->init();
@@ -530,11 +511,9 @@ public function get($name)
/**
* Returns true if the command exists, false otherwise.
*
- * @param string $name The command name or alias
- *
* @return bool true if the command exists, false otherwise
*/
- public function has($name)
+ public function has(string $name)
{
$this->init();
@@ -569,13 +548,11 @@ public function getNamespaces()
/**
* Finds a registered namespace by a name or an abbreviation.
*
- * @param string $namespace A namespace or abbreviation to search for
- *
* @return string A registered namespace
*
* @throws NamespaceNotFoundException When namespace is incorrect or ambiguous
*/
- public function findNamespace($namespace)
+ public function findNamespace(string $namespace)
{
$allNamespaces = $this->getNamespaces();
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
@@ -611,13 +588,11 @@ public function findNamespace($namespace)
* Contrary to get, this command tries to find the best
* match if you give it an abbreviation of a name or alias.
*
- * @param string $name A command name or a command alias
- *
* @return Command A Command instance
*
* @throws CommandNotFoundException When command name is incorrect or ambiguous
*/
- public function find($name)
+ public function find(string $name)
{
$this->init();
@@ -714,7 +689,7 @@ public function find($name)
$command = $this->get(reset($commands));
if ($command->isHidden()) {
- @trigger_error(sprintf('Command "%s" is hidden, finding it using an abbreviation is deprecated since Symfony 4.4, use its full name instead.', $command->getName()), E_USER_DEPRECATED);
+ throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
return $command;
@@ -725,11 +700,9 @@ public function find($name)
*
* The array keys are the full names and the values the command instances.
*
- * @param string $namespace A namespace name
- *
* @return Command[] An array of Command instances
*/
- public function all($namespace = null)
+ public function all(string $namespace = null)
{
$this->init();
@@ -769,11 +742,9 @@ public function all($namespace = null)
/**
* Returns an array of possible abbreviations given a set of names.
*
- * @param array $names An array of names
- *
- * @return array An array of abbreviations
+ * @return string[][] An array of abbreviations
*/
- public static function getAbbreviations($names)
+ public static function getAbbreviations(array $names)
{
$abbrevs = [];
foreach ($names as $name) {
@@ -786,85 +757,25 @@ public static function getAbbreviations($names)
return $abbrevs;
}
- /**
- * Renders a caught exception.
- *
- * @deprecated since Symfony 4.4, use "renderThrowable()" instead
- */
- public function renderException(\Exception $e, OutputInterface $output)
- {
- @trigger_error(sprintf('The "%s::renderException()" method is deprecated since Symfony 4.4, use "renderThrowable()" instead.', __CLASS__), E_USER_DEPRECATED);
-
- $output->writeln('', OutputInterface::VERBOSITY_QUIET);
-
- $this->doRenderException($e, $output);
-
- $this->finishRenderThrowableOrException($output);
- }
-
public function renderThrowable(\Throwable $e, OutputInterface $output): void
{
- if (__CLASS__ !== static::class && __CLASS__ === (new \ReflectionMethod($this, 'renderThrowable'))->getDeclaringClass()->getName() && __CLASS__ !== (new \ReflectionMethod($this, 'renderException'))->getDeclaringClass()->getName()) {
- @trigger_error(sprintf('The "%s::renderException()" method is deprecated since Symfony 4.4, use "renderThrowable()" instead.', __CLASS__), E_USER_DEPRECATED);
-
- if (!$e instanceof \Exception) {
- $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- }
-
- $this->renderException($e, $output);
-
- return;
- }
-
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
$this->doRenderThrowable($e, $output);
- $this->finishRenderThrowableOrException($output);
- }
-
- private function finishRenderThrowableOrException(OutputInterface $output): void
- {
if (null !== $this->runningCommand) {
$output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
}
- /**
- * @deprecated since Symfony 4.4, use "doRenderThrowable()" instead
- */
- protected function doRenderException(\Exception $e, OutputInterface $output)
- {
- @trigger_error(sprintf('The "%s::doRenderException()" method is deprecated since Symfony 4.4, use "doRenderThrowable()" instead.', __CLASS__), E_USER_DEPRECATED);
-
- $this->doActuallyRenderThrowable($e, $output);
- }
-
protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void
- {
- if (__CLASS__ !== static::class && __CLASS__ === (new \ReflectionMethod($this, 'doRenderThrowable'))->getDeclaringClass()->getName() && __CLASS__ !== (new \ReflectionMethod($this, 'doRenderException'))->getDeclaringClass()->getName()) {
- @trigger_error(sprintf('The "%s::doRenderException()" method is deprecated since Symfony 4.4, use "doRenderThrowable()" instead.', __CLASS__), E_USER_DEPRECATED);
-
- if (!$e instanceof \Exception) {
- $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- }
-
- $this->doRenderException($e, $output);
-
- return;
- }
-
- $this->doActuallyRenderThrowable($e, $output);
- }
-
- private function doActuallyRenderThrowable(\Throwable $e, OutputInterface $output): void
{
do {
$message = trim($e->getMessage());
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$class = \get_class($e);
- $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
+ $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class))).'@anonymous' : $class;
$title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
$len = Helper::strlen($title);
} else {
@@ -873,7 +784,7 @@ private function doActuallyRenderThrowable(\Throwable $e, OutputInterface $outpu
if (false !== strpos($message, "class@anonymous\0")) {
$message = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
- return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
+ return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0]))).'@anonymous' : $m[0];
}, $message);
}
@@ -1108,12 +1019,9 @@ private function getAbbreviationSuggestions(array $abbrevs): string
*
* This method is not part of public API and should not be used directly.
*
- * @param string $name The full name of the command
- * @param string $limit The maximum number of parts of the namespace
- *
* @return string The namespace of the command
*/
- public function extractNamespace($name, $limit = null)
+ public function extractNamespace(string $name, int $limit = null)
{
$parts = explode(':', $name, -1);
@@ -1171,12 +1079,9 @@ private function findAlternatives(string $name, iterable $collection): array
/**
* Sets the default Command name.
*
- * @param string $commandName The Command name
- * @param bool $isSingleCommand Set to true if there is only one command in this application
- *
* @return self
*/
- public function setDefaultCommand($commandName, $isSingleCommand = false)
+ public function setDefaultCommand(string $commandName, bool $isSingleCommand = false)
{
$this->defaultCommand = $commandName;
diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md
index 482c8cd362423..326a385055035 100644
--- a/src/Symfony/Component/Console/CHANGELOG.md
+++ b/src/Symfony/Component/Console/CHANGELOG.md
@@ -1,6 +1,28 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * `Command::setHidden()` is final since Symfony 5.1
+ * Add `SingleCommandApplication`
+
+5.0.0
+-----
+
+ * removed support for finding hidden commands using an abbreviation, use the full name instead
+ * removed `TableStyle::setCrossingChar()` method in favor of `TableStyle::setDefaultCrossingChar()`
+ * removed `TableStyle::setHorizontalBorderChar()` method in favor of `TableStyle::setDefaultCrossingChars()`
+ * removed `TableStyle::getHorizontalBorderChar()` method in favor of `TableStyle::getBorderChars()`
+ * removed `TableStyle::setVerticalBorderChar()` method in favor of `TableStyle::setVerticalBorderChars()`
+ * removed `TableStyle::getVerticalBorderChar()` method in favor of `TableStyle::getBorderChars()`
+ * removed support for returning `null` from `Command::execute()`, return `0` instead
+ * `ProcessHelper::run()` accepts only `array|Symfony\Component\Process\Process` for its `command` argument
+ * `Application::setDispatcher` accepts only `Symfony\Contracts\EventDispatcher\EventDispatcherInterface`
+ for its `dispatcher` argument
+ * renamed `Application::renderException()` and `Application::doRenderException()`
+ to `renderThrowable()` and `doRenderThrowable()` respectively.
+
4.4.0
-----
diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
index 84aceafc16657..866f3e28732e7 100644
--- a/src/Symfony/Component/Console/Command/Command.php
+++ b/src/Symfony/Component/Console/Command/Command.php
@@ -29,6 +29,9 @@
*/
class Command
{
+ public const SUCCESS = 0;
+ public const FAILURE = 1;
+
/**
* @var string|null The default command name
*/
@@ -255,7 +258,7 @@ public function run(InputInterface $input, OutputInterface $output)
$statusCode = $this->execute($input, $output);
if (!\is_int($statusCode)) {
- @trigger_error(sprintf('Return value of "%s::execute()" should always be of the type int since Symfony 4.4, %s returned.', static::class, \gettype($statusCode)), E_USER_DEPRECATED);
+ throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, %s returned.', static::class, \gettype($statusCode)));
}
}
@@ -297,7 +300,7 @@ public function setCode(callable $code)
*
* @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
*/
- public function mergeApplicationDefinition($mergeArgs = true)
+ public function mergeApplicationDefinition(bool $mergeArgs = true)
{
if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
return;
@@ -368,16 +371,14 @@ public function getNativeDefinition()
/**
* Adds an argument.
*
- * @param string $name The argument name
- * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
- * @param string $description A description text
- * @param string|string[]|null $default The default value (for InputArgument::OPTIONAL mode only)
+ * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
+ * @param string|string[]|null $default The default value (for InputArgument::OPTIONAL mode only)
*
* @throws InvalidArgumentException When argument mode is not valid
*
* @return $this
*/
- public function addArgument($name, $mode = null, $description = '', $default = null)
+ public function addArgument(string $name, int $mode = null, string $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
@@ -387,17 +388,15 @@ public function addArgument($name, $mode = null, $description = '', $default = n
/**
* Adds an option.
*
- * @param string $name The option name
- * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
- * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
- * @param string $description A description text
- * @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE)
+ * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
+ * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
+ * @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE)
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*
* @return $this
*/
- public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
+ public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
@@ -412,13 +411,11 @@ public function addOption($name, $shortcut = null, $mode = null, $description =
*
* $command->setName('foo:bar');
*
- * @param string $name The command name
- *
* @return $this
*
* @throws InvalidArgumentException When the name is invalid
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->validateName($name);
@@ -435,11 +432,9 @@ public function setName($name)
*
* PHP 5.5+ or the proctitle PECL library is required
*
- * @param string $title The process title
- *
* @return $this
*/
- public function setProcessTitle($title)
+ public function setProcessTitle(string $title)
{
$this->processTitle = $title;
@@ -458,12 +453,15 @@ public function getName()
/**
* @param bool $hidden Whether or not the command should be hidden from the list of commands
+ * The default value will be true in Symfony 6.0
*
* @return Command The current instance
+ *
+ * @final since Symfony 5.1
*/
- public function setHidden($hidden)
+ public function setHidden(bool $hidden /*= true*/)
{
- $this->hidden = (bool) $hidden;
+ $this->hidden = $hidden;
return $this;
}
@@ -479,11 +477,9 @@ public function isHidden()
/**
* Sets the description for the command.
*
- * @param string $description The description for the command
- *
* @return $this
*/
- public function setDescription($description)
+ public function setDescription(string $description)
{
$this->description = $description;
@@ -503,11 +499,9 @@ public function getDescription()
/**
* Sets the help for the command.
*
- * @param string $help The help for the command
- *
* @return $this
*/
- public function setHelp($help)
+ public function setHelp(string $help)
{
$this->help = $help;
@@ -556,12 +550,8 @@ public function getProcessedHelp()
*
* @throws InvalidArgumentException When an alias is invalid
*/
- public function setAliases($aliases)
+ public function setAliases(iterable $aliases)
{
- if (!\is_array($aliases) && !$aliases instanceof \Traversable) {
- throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
- }
-
foreach ($aliases as $alias) {
$this->validateName($alias);
}
@@ -588,7 +578,7 @@ public function getAliases()
*
* @return string The synopsis
*/
- public function getSynopsis($short = false)
+ public function getSynopsis(bool $short = false)
{
$key = $short ? 'short' : 'long';
@@ -600,13 +590,11 @@ public function getSynopsis($short = false)
}
/**
- * Add a command usage example.
- *
- * @param string $usage The usage, it'll be prefixed with the command name
+ * Add a command usage example, it'll be prefixed with the command name.
*
* @return $this
*/
- public function addUsage($usage)
+ public function addUsage(string $usage)
{
if (0 !== strpos($usage, $this->name)) {
$usage = sprintf('%s %s', $this->name, $usage);
@@ -630,14 +618,12 @@ public function getUsages()
/**
* Gets a helper instance by name.
*
- * @param string $name The helper name
- *
* @return mixed The helper value
*
* @throws LogicException if no HelperSet is defined
* @throws InvalidArgumentException if the helper is not defined
*/
- public function getHelper($name)
+ public function getHelper(string $name)
{
if (null === $this->helperSet) {
throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
diff --git a/src/Symfony/Component/Console/CommandLoader/CommandLoaderInterface.php b/src/Symfony/Component/Console/CommandLoader/CommandLoaderInterface.php
index ca1029cb60042..d4f44e88fd974 100644
--- a/src/Symfony/Component/Console/CommandLoader/CommandLoaderInterface.php
+++ b/src/Symfony/Component/Console/CommandLoader/CommandLoaderInterface.php
@@ -22,22 +22,18 @@ interface CommandLoaderInterface
/**
* Loads a command.
*
- * @param string $name
- *
* @return Command
*
* @throws CommandNotFoundException
*/
- public function get($name);
+ public function get(string $name);
/**
* Checks if a command exists.
*
- * @param string $name
- *
* @return bool
*/
- public function has($name);
+ public function has(string $name);
/**
* @return string[] All registered command names
diff --git a/src/Symfony/Component/Console/CommandLoader/ContainerCommandLoader.php b/src/Symfony/Component/Console/CommandLoader/ContainerCommandLoader.php
index 50e5950a4d6de..ddccb3d45f416 100644
--- a/src/Symfony/Component/Console/CommandLoader/ContainerCommandLoader.php
+++ b/src/Symfony/Component/Console/CommandLoader/ContainerCommandLoader.php
@@ -36,7 +36,7 @@ public function __construct(ContainerInterface $container, array $commandMap)
/**
* {@inheritdoc}
*/
- public function get($name)
+ public function get(string $name)
{
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
@@ -48,7 +48,7 @@ public function get($name)
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
}
diff --git a/src/Symfony/Component/Console/CommandLoader/FactoryCommandLoader.php b/src/Symfony/Component/Console/CommandLoader/FactoryCommandLoader.php
index d9c2055710968..7e2db346471a2 100644
--- a/src/Symfony/Component/Console/CommandLoader/FactoryCommandLoader.php
+++ b/src/Symfony/Component/Console/CommandLoader/FactoryCommandLoader.php
@@ -33,7 +33,7 @@ public function __construct(array $factories)
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
return isset($this->factories[$name]);
}
@@ -41,7 +41,7 @@ public function has($name)
/**
* {@inheritdoc}
*/
- public function get($name)
+ public function get(string $name)
{
if (!isset($this->factories[$name])) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
diff --git a/src/Symfony/Component/Console/Descriptor/Descriptor.php b/src/Symfony/Component/Console/Descriptor/Descriptor.php
index d25a708e479ee..df85e38105133 100644
--- a/src/Symfony/Component/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Component/Console/Descriptor/Descriptor.php
@@ -61,11 +61,8 @@ public function describe(OutputInterface $output, $object, array $options = [])
/**
* Writes content to output.
- *
- * @param string $content
- * @param bool $decorated
*/
- protected function write($content, $decorated = false)
+ protected function write(string $content, bool $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
diff --git a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
index 02b8c30125b95..8b3e182efc358 100644
--- a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
+++ b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
@@ -44,7 +44,7 @@ public function describe(OutputInterface $output, $object, array $options = [])
/**
* {@inheritdoc}
*/
- protected function write($content, $decorated = true)
+ protected function write(string $content, bool $decorated = true)
{
parent::write($content, $decorated);
}
@@ -92,7 +92,9 @@ protected function describeInputDefinition(InputDefinition $definition, array $o
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
- $this->write($this->describeInputArgument($argument));
+ if (null !== $describeInputArgument = $this->describeInputArgument($argument)) {
+ $this->write($describeInputArgument);
+ }
}
}
@@ -104,7 +106,9 @@ protected function describeInputDefinition(InputDefinition $definition, array $o
$this->write('### Options');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
- $this->write($this->describeInputOption($option));
+ if (null !== $describeInputOption = $this->describeInputOption($option)) {
+ $this->write($describeInputOption);
+ }
}
}
}
@@ -163,7 +167,9 @@ protected function describeApplication(Application $application, array $options
foreach ($description->getCommands() as $command) {
$this->write("\n\n");
- $this->write($this->describeCommand($command));
+ if (null !== $describeCommand = $this->describeCommand($command)) {
+ $this->write($describeCommand);
+ }
}
}
diff --git a/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php b/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php
index 79a51906a8768..a24697cdc950a 100644
--- a/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php
+++ b/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php
@@ -15,10 +15,8 @@
* Allows to do things before the command is executed, like skipping the command or changing the input.
*
* @author Fabien Potencier
- *
- * @final since Symfony 4.4
*/
-class ConsoleCommandEvent extends ConsoleEvent
+final class ConsoleCommandEvent extends ConsoleEvent
{
/**
* The return code for skipped commands, this will also be passed into the terminate event.
@@ -32,30 +30,21 @@ class ConsoleCommandEvent extends ConsoleEvent
/**
* Disables the command, so it won't be run.
- *
- * @return bool
*/
- public function disableCommand()
+ public function disableCommand(): bool
{
return $this->commandShouldRun = false;
}
- /**
- * Enables the command.
- *
- * @return bool
- */
- public function enableCommand()
+ public function enableCommand(): bool
{
return $this->commandShouldRun = true;
}
/**
* Returns true if the command is runnable, false otherwise.
- *
- * @return bool
*/
- public function commandShouldRun()
+ public function commandShouldRun(): bool
{
return $this->commandShouldRun;
}
diff --git a/src/Symfony/Component/Console/Event/ConsoleEvent.php b/src/Symfony/Component/Console/Event/ConsoleEvent.php
index 5440da216c96f..89ab645594ce1 100644
--- a/src/Symfony/Component/Console/Event/ConsoleEvent.php
+++ b/src/Symfony/Component/Console/Event/ConsoleEvent.php
@@ -14,7 +14,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\EventDispatcher\Event;
+use Symfony\Contracts\EventDispatcher\Event;
/**
* Allows to inspect input and output of a command.
diff --git a/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php b/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php
index 43d0f8ab1a2ba..190038d1a27c9 100644
--- a/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php
+++ b/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php
@@ -19,10 +19,8 @@
* Allows to manipulate the exit code of a command after its execution.
*
* @author Francesco Levorato
- *
- * @final since Symfony 4.4
*/
-class ConsoleTerminateEvent extends ConsoleEvent
+final class ConsoleTerminateEvent extends ConsoleEvent
{
private $exitCode;
@@ -33,22 +31,12 @@ public function __construct(Command $command, InputInterface $input, OutputInter
$this->setExitCode($exitCode);
}
- /**
- * Sets the exit code.
- *
- * @param int $exitCode The command exit code
- */
- public function setExitCode($exitCode)
+ public function setExitCode(int $exitCode): void
{
- $this->exitCode = (int) $exitCode;
+ $this->exitCode = $exitCode;
}
- /**
- * Gets the exit code.
- *
- * @return int The command exit code
- */
- public function getExitCode()
+ public function getExitCode(): int
{
return $this->exitCode;
}
diff --git a/src/Symfony/Component/Console/Formatter/NullOutputFormatter.php b/src/Symfony/Component/Console/Formatter/NullOutputFormatter.php
new file mode 100644
index 0000000000000..0aa0a5c252327
--- /dev/null
+++ b/src/Symfony/Component/Console/Formatter/NullOutputFormatter.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Formatter;
+
+/**
+ * @author Tien Xuan Vo
+ */
+final class NullOutputFormatter implements OutputFormatterInterface
+{
+ private $style;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function format(?string $message): void
+ {
+ // do nothing
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStyle(string $name): OutputFormatterStyleInterface
+ {
+ if ($this->style) {
+ return $this->style;
+ }
+ // to comply with the interface we must return a OutputFormatterStyleInterface
+ return $this->style = new NullOutputFormatterStyle();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasStyle(string $name): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isDecorated(): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDecorated(bool $decorated): void
+ {
+ // do nothing
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStyle(string $name, OutputFormatterStyleInterface $style): void
+ {
+ // do nothing
+ }
+}
diff --git a/src/Symfony/Component/Console/Formatter/NullOutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/NullOutputFormatterStyle.php
new file mode 100644
index 0000000000000..bfd0afedd47d8
--- /dev/null
+++ b/src/Symfony/Component/Console/Formatter/NullOutputFormatterStyle.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Formatter;
+
+/**
+ * @author Tien Xuan Vo
+ */
+final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(string $text): string
+ {
+ return $text;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setBackground(string $color = null): void
+ {
+ // do nothing
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setForeground(string $color = null): void
+ {
+ // do nothing
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setOption(string $option): void
+ {
+ // do nothing
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setOptions(array $options): void
+ {
+ // do nothing
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unsetOption(string $option): void
+ {
+ // do nothing
+ }
+}
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php
index d0673e74564da..2ca63b6f17d83 100644
--- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php
@@ -28,11 +28,9 @@ class OutputFormatter implements WrappableOutputFormatterInterface
/**
* Escapes "<" special char in given text.
*
- * @param string $text Text to escape
- *
* @return string Escaped text
*/
- public static function escape($text)
+ public static function escape(string $text)
{
$text = preg_replace('/([^\\\\]?)', '$1\\<', $text);
@@ -80,9 +78,9 @@ public function __construct(bool $decorated = false, array $styles = [])
/**
* {@inheritdoc}
*/
- public function setDecorated($decorated)
+ public function setDecorated(bool $decorated)
{
- $this->decorated = (bool) $decorated;
+ $this->decorated = $decorated;
}
/**
@@ -96,7 +94,7 @@ public function isDecorated()
/**
* {@inheritdoc}
*/
- public function setStyle($name, OutputFormatterStyleInterface $style)
+ public function setStyle(string $name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
}
@@ -104,7 +102,7 @@ public function setStyle($name, OutputFormatterStyleInterface $style)
/**
* {@inheritdoc}
*/
- public function hasStyle($name)
+ public function hasStyle(string $name)
{
return isset($this->styles[strtolower($name)]);
}
@@ -112,7 +110,7 @@ public function hasStyle($name)
/**
* {@inheritdoc}
*/
- public function getStyle($name)
+ public function getStyle(string $name)
{
if (!$this->hasStyle($name)) {
throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
@@ -124,15 +122,15 @@ public function getStyle($name)
/**
* {@inheritdoc}
*/
- public function format($message)
+ public function format(?string $message)
{
- return $this->formatAndWrap((string) $message, 0);
+ return $this->formatAndWrap($message, 0);
}
/**
* {@inheritdoc}
*/
- public function formatAndWrap(string $message, int $width)
+ public function formatAndWrap(?string $message, int $width)
{
$offset = 0;
$output = '';
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php b/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php
index 22f40a34edccf..8c50d41964d76 100644
--- a/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php
@@ -20,10 +20,8 @@ interface OutputFormatterInterface
{
/**
* Sets the decorated flag.
- *
- * @param bool $decorated Whether to decorate the messages or not
*/
- public function setDecorated($decorated);
+ public function setDecorated(bool $decorated);
/**
* Gets the decorated flag.
@@ -34,37 +32,27 @@ public function isDecorated();
/**
* Sets a new style.
- *
- * @param string $name The style name
*/
- public function setStyle($name, OutputFormatterStyleInterface $style);
+ public function setStyle(string $name, OutputFormatterStyleInterface $style);
/**
* Checks if output formatter has style with specified name.
*
- * @param string $name
- *
* @return bool
*/
- public function hasStyle($name);
+ public function hasStyle(string $name);
/**
* Gets style options from style with specified name.
*
- * @param string $name
- *
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style isn't defined
*/
- public function getStyle($name);
+ public function getStyle(string $name);
/**
* Formats a message according to the given styles.
- *
- * @param string $message The message to style
- *
- * @return string The styled message
*/
- public function format($message);
+ public function format(?string $message);
}
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php
index 16994202ef8df..c5d3ea109814f 100644
--- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php
@@ -78,7 +78,7 @@ public function __construct(string $foreground = null, string $background = null
/**
* {@inheritdoc}
*/
- public function setForeground($color = null)
+ public function setForeground(string $color = null)
{
if (null === $color) {
$this->foreground = null;
@@ -96,7 +96,7 @@ public function setForeground($color = null)
/**
* {@inheritdoc}
*/
- public function setBackground($color = null)
+ public function setBackground(string $color = null)
{
if (null === $color) {
$this->background = null;
@@ -119,7 +119,7 @@ public function setHref(string $url): void
/**
* {@inheritdoc}
*/
- public function setOption($option)
+ public function setOption(string $option)
{
if (!isset(static::$availableOptions[$option])) {
throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
@@ -133,7 +133,7 @@ public function setOption($option)
/**
* {@inheritdoc}
*/
- public function unsetOption($option)
+ public function unsetOption(string $option)
{
if (!isset(static::$availableOptions[$option])) {
throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
@@ -160,7 +160,7 @@ public function setOptions(array $options)
/**
* {@inheritdoc}
*/
- public function apply($text)
+ public function apply(string $text)
{
$setCodes = [];
$unsetCodes = [];
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php
index af171c27020c9..b30560d22e161 100644
--- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php
@@ -20,31 +20,23 @@ interface OutputFormatterStyleInterface
{
/**
* Sets style foreground color.
- *
- * @param string|null $color The color name
*/
- public function setForeground($color = null);
+ public function setForeground(string $color = null);
/**
* Sets style background color.
- *
- * @param string $color The color name
*/
- public function setBackground($color = null);
+ public function setBackground(string $color = null);
/**
* Sets some specific style option.
- *
- * @param string $option The option name
*/
- public function setOption($option);
+ public function setOption(string $option);
/**
* Unsets some specific style option.
- *
- * @param string $option The option name
*/
- public function unsetOption($option);
+ public function unsetOption(string $option);
/**
* Sets multiple style options at once.
@@ -54,9 +46,7 @@ public function setOptions(array $options);
/**
* Applies the style to a given text.
*
- * @param string $text The text to style
- *
* @return string
*/
- public function apply($text);
+ public function apply(string $text);
}
diff --git a/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php b/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php
index 6694053f057ea..42319ee5543f0 100644
--- a/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php
+++ b/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php
@@ -21,5 +21,5 @@ interface WrappableOutputFormatterInterface extends OutputFormatterInterface
/**
* Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping).
*/
- public function formatAndWrap(string $message, int $width);
+ public function formatAndWrap(?string $message, int $width);
}
diff --git a/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php b/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php
index 1653edeb1fce3..9d07ec2441eac 100644
--- a/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php
+++ b/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php
@@ -27,13 +27,9 @@ class DebugFormatterHelper extends Helper
/**
* Starts a debug formatting session.
*
- * @param string $id The id of the formatting session
- * @param string $message The message to display
- * @param string $prefix The prefix to use
- *
* @return string
*/
- public function start($id, $message, $prefix = 'RUN')
+ public function start(string $id, string $message, string $prefix = 'RUN')
{
$this->started[$id] = ['border' => ++$this->count % \count($this->colors)];
@@ -43,15 +39,9 @@ public function start($id, $message, $prefix = 'RUN')
/**
* Adds progress to a formatting session.
*
- * @param string $id The id of the formatting session
- * @param string $buffer The message to display
- * @param bool $error Whether to consider the buffer as error
- * @param string $prefix The prefix for output
- * @param string $errorPrefix The prefix for error output
- *
* @return string
*/
- public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR')
+ public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR')
{
$message = '';
@@ -85,14 +75,9 @@ public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPr
/**
* Stops a formatting session.
*
- * @param string $id The id of the formatting session
- * @param string $message The message to display
- * @param bool $successful Whether to consider the result as success
- * @param string $prefix The prefix for the end output
- *
* @return string
*/
- public function stop($id, $message, $successful, $prefix = 'RES')
+ public function stop(string $id, string $message, bool $successful, string $prefix = 'RES')
{
$trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
diff --git a/src/Symfony/Component/Console/Helper/DescriptorHelper.php b/src/Symfony/Component/Console/Helper/DescriptorHelper.php
index 3055baefd432b..f2ad9db7a194c 100644
--- a/src/Symfony/Component/Console/Helper/DescriptorHelper.php
+++ b/src/Symfony/Component/Console/Helper/DescriptorHelper.php
@@ -48,11 +48,9 @@ public function __construct()
* * format: string, the output format name
* * raw_text: boolean, sets output type as raw
*
- * @param object $object
- *
* @throws InvalidArgumentException when the given format is not supported
*/
- public function describe(OutputInterface $output, $object, array $options = [])
+ public function describe(OutputInterface $output, ?object $object, array $options = [])
{
$options = array_merge([
'raw_text' => false,
@@ -70,11 +68,9 @@ public function describe(OutputInterface $output, $object, array $options = [])
/**
* Registers a descriptor.
*
- * @param string $format
- *
* @return $this
*/
- public function register($format, DescriptorInterface $descriptor)
+ public function register(string $format, DescriptorInterface $descriptor)
{
$this->descriptors[$format] = $descriptor;
diff --git a/src/Symfony/Component/Console/Helper/FormatterHelper.php b/src/Symfony/Component/Console/Helper/FormatterHelper.php
index d6eccee8e85ac..a505415cffdc6 100644
--- a/src/Symfony/Component/Console/Helper/FormatterHelper.php
+++ b/src/Symfony/Component/Console/Helper/FormatterHelper.php
@@ -23,13 +23,9 @@ class FormatterHelper extends Helper
/**
* Formats a message within a section.
*
- * @param string $section The section name
- * @param string $message The message
- * @param string $style The style to apply to the section
- *
* @return string The format section
*/
- public function formatSection($section, $message, $style = 'info')
+ public function formatSection(string $section, string $message, string $style = 'info')
{
return sprintf('<%s>[%s]%s> %s', $style, $section, $style, $message);
}
@@ -38,12 +34,10 @@ public function formatSection($section, $message, $style = 'info')
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
- * @param string $style The style to apply to the whole block
- * @param bool $large Whether to return a large block
*
* @return string The formatter message
*/
- public function formatBlock($messages, $style, $large = false)
+ public function formatBlock($messages, string $style, bool $large = false)
{
if (!\is_array($messages)) {
$messages = [$messages];
@@ -75,13 +69,9 @@ public function formatBlock($messages, $style, $large = false)
/**
* Truncates a message to the given length.
*
- * @param string $message
- * @param int $length
- * @param string $suffix
- *
* @return string
*/
- public function truncate($message, $length, $suffix = '...')
+ public function truncate(string $message, int $length, string $suffix = '...')
{
$computedLength = $length - self::strlen($suffix);
diff --git a/src/Symfony/Component/Console/Helper/Helper.php b/src/Symfony/Component/Console/Helper/Helper.php
index 0ddddf6bc5023..e52e31515e968 100644
--- a/src/Symfony/Component/Console/Helper/Helper.php
+++ b/src/Symfony/Component/Console/Helper/Helper.php
@@ -41,11 +41,9 @@ public function getHelperSet()
/**
* Returns the length of a string, using mb_strwidth if it is available.
*
- * @param string $string The string to check its length
- *
* @return int The length of the string
*/
- public static function strlen($string)
+ public static function strlen(?string $string)
{
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
@@ -57,13 +55,9 @@ public static function strlen($string)
/**
* Returns the subset of a string, using mb_substr if it is available.
*
- * @param string $string String to subset
- * @param int $from Start offset
- * @param int|null $length Length to read
- *
* @return string The string subset
*/
- public static function substr($string, $from, $length = null)
+ public static function substr(string $string, int $from, int $length = null)
{
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return substr($string, $from, $length);
@@ -101,7 +95,7 @@ public static function formatTime($secs)
}
}
- public static function formatMemory($memory)
+ public static function formatMemory(int $memory)
{
if ($memory >= 1024 * 1024 * 1024) {
return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
diff --git a/src/Symfony/Component/Console/Helper/HelperSet.php b/src/Symfony/Component/Console/Helper/HelperSet.php
index d9d73f25fc69a..5c08a7606d289 100644
--- a/src/Symfony/Component/Console/Helper/HelperSet.php
+++ b/src/Symfony/Component/Console/Helper/HelperSet.php
@@ -37,12 +37,7 @@ public function __construct(array $helpers = [])
}
}
- /**
- * Sets a helper.
- *
- * @param string $alias An alias
- */
- public function set(HelperInterface $helper, $alias = null)
+ public function set(HelperInterface $helper, string $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
@@ -55,11 +50,9 @@ public function set(HelperInterface $helper, $alias = null)
/**
* Returns true if the helper if defined.
*
- * @param string $name The helper name
- *
* @return bool true if the helper is defined, false otherwise
*/
- public function has($name)
+ public function has(string $name)
{
return isset($this->helpers[$name]);
}
@@ -67,13 +60,11 @@ public function has($name)
/**
* Gets a helper value.
*
- * @param string $name The helper name
- *
* @return HelperInterface The helper instance
*
* @throws InvalidArgumentException if the helper is not defined
*/
- public function get($name)
+ public function get(string $name)
{
if (!$this->has($name)) {
throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
diff --git a/src/Symfony/Component/Console/Helper/ProcessHelper.php b/src/Symfony/Component/Console/Helper/ProcessHelper.php
index 5f512d200d4a2..944c5939576e3 100644
--- a/src/Symfony/Component/Console/Helper/ProcessHelper.php
+++ b/src/Symfony/Component/Console/Helper/ProcessHelper.php
@@ -21,22 +21,20 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.2
+ * @final
*/
class ProcessHelper extends Helper
{
/**
* Runs an external process.
*
- * @param array|Process $cmd An instance of Process or an array of the command and arguments
- * @param string|null $error An error message that must be displayed if something went wrong
- * @param callable|null $callback A PHP callback to run whenever there is some
- * output available on STDOUT or STDERR
- * @param int $verbosity The threshold for verbosity
+ * @param array|Process $cmd An instance of Process or an array of the command and arguments
+ * @param callable|null $callback A PHP callback to run whenever there is some
+ * output available on STDOUT or STDERR
*
* @return Process The process that ran
*/
- public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE)
+ public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
@@ -49,8 +47,7 @@ public function run(OutputInterface $output, $cmd, $error = null, callable $call
}
if (!\is_array($cmd)) {
- @trigger_error(sprintf('Passing a command as a string to "%s()" is deprecated since Symfony 4.2, pass it the command as an array of arguments instead.', __METHOD__), E_USER_DEPRECATED);
- $cmd = [method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($cmd) : new Process($cmd)];
+ throw new \TypeError(sprintf('The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, \is_object($cmd) ? \get_class($cmd) : \gettype($cmd)));
}
if (\is_string($cmd[0] ?? null)) {
@@ -92,7 +89,6 @@ public function run(OutputInterface $output, $cmd, $error = null, callable $call
* exits with a non-zero exit code.
*
* @param string|Process $cmd An instance of Process or a command to run
- * @param string|null $error An error message that must be displayed if something went wrong
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
*
@@ -102,7 +98,7 @@ public function run(OutputInterface $output, $cmd, $error = null, callable $call
*
* @see run()
*/
- public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null)
+ public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process
{
$process = $this->run($output, $cmd, $error, $callback);
@@ -115,10 +111,8 @@ public function mustRun(OutputInterface $output, $cmd, $error = null, callable $
/**
* Wraps a Process callback to add debugging output.
- *
- * @return callable
*/
- public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null)
+ public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
@@ -143,7 +137,7 @@ private function escapeString(string $str): string
/**
* {@inheritdoc}
*/
- public function getName()
+ public function getName(): string
{
return 'process';
}
diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php
index e4f0a9936f31e..83c7b7dd3bbc5 100644
--- a/src/Symfony/Component/Console/Helper/ProgressBar.php
+++ b/src/Symfony/Component/Console/Helper/ProgressBar.php
@@ -191,11 +191,29 @@ public function getProgressPercent(): float
return $this->percent;
}
- public function getBarOffset(): int
+ public function getBarOffset(): float
{
return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? min(5, $this->barWidth / 15) * $this->writeCount : $this->step) % $this->barWidth);
}
+ public function getEstimated(): float
+ {
+ if (!$this->step) {
+ return 0;
+ }
+
+ return round((time() - $this->startTime) / $this->step * $this->max);
+ }
+
+ public function getRemaining(): float
+ {
+ if (!$this->step) {
+ return 0;
+ }
+
+ return round((time() - $this->startTime) / $this->step * ($this->max - $this->step));
+ }
+
public function setBarWidth(int $size)
{
$this->barWidth = max(1, $size);
@@ -500,26 +518,14 @@ private static function initPlaceholderFormatters(): array
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}
- if (!$bar->getProgress()) {
- $remaining = 0;
- } else {
- $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
- }
-
- return Helper::formatTime($remaining);
+ return Helper::formatTime($bar->getRemaining());
},
'estimated' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}
- if (!$bar->getProgress()) {
- $estimated = 0;
- } else {
- $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
- }
-
- return Helper::formatTime($estimated);
+ return Helper::formatTime($bar->getEstimated());
},
'memory' => function (self $bar) {
return Helper::formatMemory(memory_get_usage(true));
diff --git a/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/src/Symfony/Component/Console/Helper/ProgressIndicator.php
index 04db8f7c16c40..81cb783ea484b 100644
--- a/src/Symfony/Component/Console/Helper/ProgressIndicator.php
+++ b/src/Symfony/Component/Console/Helper/ProgressIndicator.php
@@ -34,9 +34,8 @@ class ProgressIndicator
private static $formats;
/**
- * @param string|null $format Indicator format
- * @param int $indicatorChangeInterval Change interval in milliseconds
- * @param array|null $indicatorValues Animated indicator characters
+ * @param int $indicatorChangeInterval Change interval in milliseconds
+ * @param array|null $indicatorValues Animated indicator characters
*/
public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
{
@@ -64,10 +63,8 @@ public function __construct(OutputInterface $output, string $format = null, int
/**
* Sets the current indicator message.
- *
- * @param string|null $message
*/
- public function setMessage($message)
+ public function setMessage(?string $message)
{
$this->message = $message;
@@ -76,10 +73,8 @@ public function setMessage($message)
/**
* Starts the indicator output.
- *
- * @param $message
*/
- public function start($message)
+ public function start(string $message)
{
if ($this->started) {
throw new LogicException('Progress indicator already started.');
@@ -124,7 +119,7 @@ public function advance()
*
* @param $message
*/
- public function finish($message)
+ public function finish(string $message)
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
@@ -139,11 +134,9 @@ public function finish($message)
/**
* Gets the format for a given name.
*
- * @param string $name The format name
- *
* @return string|null A format string
*/
- public static function getFormatDefinition($name)
+ public static function getFormatDefinition(string $name)
{
if (!self::$formats) {
self::$formats = self::initFormats();
@@ -156,11 +149,8 @@ public static function getFormatDefinition($name)
* Sets a placeholder formatter for a given name.
*
* This method also allow you to override an existing placeholder.
- *
- * @param string $name The placeholder name (including the delimiter char like %)
- * @param callable $callable A PHP callable
*/
- public static function setPlaceholderFormatterDefinition($name, $callable)
+ public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
@@ -170,13 +160,11 @@ public static function setPlaceholderFormatterDefinition($name, $callable)
}
/**
- * Gets the placeholder formatter for a given name.
- *
- * @param string $name The placeholder name (including the delimiter char like %)
+ * Gets the placeholder formatter for a given name (including the delimiter char like %).
*
* @return callable|null A PHP callable
*/
- public static function getPlaceholderFormatterDefinition($name)
+ public static function getPlaceholderFormatterDefinition(string $name)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php
index 40709cedd929d..f95023be976d0 100644
--- a/src/Symfony/Component/Console/Helper/QuestionHelper.php
+++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php
@@ -22,6 +22,7 @@
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
+use function Symfony\Component\String\s;
/**
* The QuestionHelper class provides helpers to interact with the user.
@@ -177,11 +178,9 @@ protected function writePrompt(OutputInterface $output, Question $question)
}
/**
- * @param string $tag
- *
* @return string[]
*/
- protected function formatChoiceQuestionChoices(ChoiceQuestion $question, $tag)
+ protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag)
{
$messages = [];
@@ -244,9 +243,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
} elseif ("\177" === $c) { // Backspace Character
if (0 === $numMatches && 0 !== $i) {
--$i;
- $fullChoice = self::substr($fullChoice, 0, $i);
// Move cursor backwards
- $output->write("\033[1D");
+ $output->write(sprintf("\033[%dD", s($fullChoice)->slice(-1)->width(false)));
+
+ $fullChoice = self::substr($fullChoice, 0, $i);
}
if (0 === $i) {
diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php
index e4a7423a1886c..5aefa3d7b9e02 100644
--- a/src/Symfony/Component/Console/Helper/Table.php
+++ b/src/Symfony/Component/Console/Helper/Table.php
@@ -102,10 +102,8 @@ public function __construct(OutputInterface $output)
/**
* Sets a style definition.
- *
- * @param string $name The style name
*/
- public static function setStyleDefinition($name, TableStyle $style)
+ public static function setStyleDefinition(string $name, TableStyle $style)
{
if (!self::$styles) {
self::$styles = self::initStyles();
@@ -117,11 +115,9 @@ public static function setStyleDefinition($name, TableStyle $style)
/**
* Gets a style definition by name.
*
- * @param string $name The style name
- *
* @return TableStyle
*/
- public static function getStyleDefinition($name)
+ public static function getStyleDefinition(string $name)
{
if (!self::$styles) {
self::$styles = self::initStyles();
@@ -161,15 +157,12 @@ public function getStyle()
/**
* Sets table column style.
*
- * @param int $columnIndex Column index
- * @param TableStyle|string $name The style name or a TableStyle instance
+ * @param TableStyle|string $name The style name or a TableStyle instance
*
* @return $this
*/
- public function setColumnStyle($columnIndex, $name)
+ public function setColumnStyle(int $columnIndex, $name)
{
- $columnIndex = (int) $columnIndex;
-
$this->columnStyles[$columnIndex] = $this->resolveStyle($name);
return $this;
@@ -180,11 +173,9 @@ public function setColumnStyle($columnIndex, $name)
*
* If style was not set, it returns the global table style.
*
- * @param int $columnIndex Column index
- *
* @return TableStyle
*/
- public function getColumnStyle($columnIndex)
+ public function getColumnStyle(int $columnIndex)
{
return $this->columnStyles[$columnIndex] ?? $this->getStyle();
}
@@ -192,14 +183,11 @@ public function getColumnStyle($columnIndex)
/**
* Sets the minimum width of a column.
*
- * @param int $columnIndex Column index
- * @param int $width Minimum column width in characters
- *
* @return $this
*/
- public function setColumnWidth($columnIndex, $width)
+ public function setColumnWidth(int $columnIndex, int $width)
{
- $this->columnWidths[(int) $columnIndex] = (int) $width;
+ $this->columnWidths[$columnIndex] = $width;
return $this;
}
diff --git a/src/Symfony/Component/Console/Helper/TableStyle.php b/src/Symfony/Component/Console/Helper/TableStyle.php
index b8f75e7dbd34d..83719424cfbf5 100644
--- a/src/Symfony/Component/Console/Helper/TableStyle.php
+++ b/src/Symfony/Component/Console/Helper/TableStyle.php
@@ -51,11 +51,9 @@ class TableStyle
/**
* Sets padding character, used for cell padding.
*
- * @param string $paddingChar
- *
* @return $this
*/
- public function setPaddingChar($paddingChar)
+ public function setPaddingChar(string $paddingChar)
{
if (!$paddingChar) {
throw new LogicException('The padding char must not be empty');
@@ -89,9 +87,6 @@ public function getPaddingChar()
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
* ╚═══════════════╧══════════════════════════╧══════════════════╝
*
- *
- * @param string $outside Outside border char (see #1 of example)
- * @param string|null $inside Inside border char (see #2 of example), equals $outside if null
*/
public function setHorizontalBorderChars(string $outside, string $inside = null): self
{
@@ -101,36 +96,6 @@ public function setHorizontalBorderChars(string $outside, string $inside = null)
return $this;
}
- /**
- * Sets horizontal border character.
- *
- * @param string $horizontalBorderChar
- *
- * @return $this
- *
- * @deprecated since Symfony 4.1, use {@link setHorizontalBorderChars()} instead.
- */
- public function setHorizontalBorderChar($horizontalBorderChar)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->setHorizontalBorderChars($horizontalBorderChar, $horizontalBorderChar);
- }
-
- /**
- * Gets horizontal border character.
- *
- * @return string
- *
- * @deprecated since Symfony 4.1, use {@link getBorderChars()} instead.
- */
- public function getHorizontalBorderChar()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->horizontalOutsideBorderChar;
- }
-
/**
* Sets vertical border characters.
*
@@ -145,9 +110,6 @@ public function getHorizontalBorderChar()
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
* ╚═══════════════╧══════════════════════════╧══════════════════╝
*
- *
- * @param string $outside Outside border char (see #1 of example)
- * @param string|null $inside Inside border char (see #2 of example), equals $outside if null
*/
public function setVerticalBorderChars(string $outside, string $inside = null): self
{
@@ -157,36 +119,6 @@ public function setVerticalBorderChars(string $outside, string $inside = null):
return $this;
}
- /**
- * Sets vertical border character.
- *
- * @param string $verticalBorderChar
- *
- * @return $this
- *
- * @deprecated since Symfony 4.1, use {@link setVerticalBorderChars()} instead.
- */
- public function setVerticalBorderChar($verticalBorderChar)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->setVerticalBorderChars($verticalBorderChar, $verticalBorderChar);
- }
-
- /**
- * Gets vertical border character.
- *
- * @return string
- *
- * @deprecated since Symfony 4.1, use {@link getBorderChars()} instead.
- */
- public function getVerticalBorderChar()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->verticalOutsideBorderChar;
- }
-
/**
* Gets border characters.
*
@@ -259,22 +191,6 @@ public function setDefaultCrossingChar(string $char): self
return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char);
}
- /**
- * Sets crossing character.
- *
- * @param string $crossingChar
- *
- * @return $this
- *
- * @deprecated since Symfony 4.1. Use {@link setDefaultCrossingChar()} instead.
- */
- public function setCrossingChar($crossingChar)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->setDefaultCrossingChar($crossingChar);
- }
-
/**
* Gets crossing character.
*
@@ -311,11 +227,9 @@ public function getCrossingChars(): array
/**
* Sets header cell format.
*
- * @param string $cellHeaderFormat
- *
* @return $this
*/
- public function setCellHeaderFormat($cellHeaderFormat)
+ public function setCellHeaderFormat(string $cellHeaderFormat)
{
$this->cellHeaderFormat = $cellHeaderFormat;
@@ -335,11 +249,9 @@ public function getCellHeaderFormat()
/**
* Sets row cell format.
*
- * @param string $cellRowFormat
- *
* @return $this
*/
- public function setCellRowFormat($cellRowFormat)
+ public function setCellRowFormat(string $cellRowFormat)
{
$this->cellRowFormat = $cellRowFormat;
@@ -359,11 +271,9 @@ public function getCellRowFormat()
/**
* Sets row cell content format.
*
- * @param string $cellRowContentFormat
- *
* @return $this
*/
- public function setCellRowContentFormat($cellRowContentFormat)
+ public function setCellRowContentFormat(string $cellRowContentFormat)
{
$this->cellRowContentFormat = $cellRowContentFormat;
@@ -383,11 +293,9 @@ public function getCellRowContentFormat()
/**
* Sets table border format.
*
- * @param string $borderFormat
- *
* @return $this
*/
- public function setBorderFormat($borderFormat)
+ public function setBorderFormat(string $borderFormat)
{
$this->borderFormat = $borderFormat;
@@ -407,11 +315,9 @@ public function getBorderFormat()
/**
* Sets cell padding type.
*
- * @param int $padType STR_PAD_*
- *
* @return $this
*/
- public function setPadType($padType)
+ public function setPadType(int $padType)
{
if (!\in_array($padType, [STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH], true)) {
throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php
index 3bc20555362ed..cbd9f41f2f868 100644
--- a/src/Symfony/Component/Console/Input/ArgvInput.php
+++ b/src/Symfony/Component/Console/Input/ArgvInput.php
@@ -43,9 +43,6 @@ class ArgvInput extends Input
private $tokens;
private $parsed;
- /**
- * @param array|null $argv An array of parameters from the CLI (in the argv format)
- */
public function __construct(array $argv = null, InputDefinition $definition = null)
{
if (null === $argv) {
@@ -275,7 +272,7 @@ public function getFirstArgument()
/**
* {@inheritdoc}
*/
- public function hasParameterOption($values, $onlyParams = false)
+ public function hasParameterOption($values, bool $onlyParams = false)
{
$values = (array) $values;
@@ -300,7 +297,7 @@ public function hasParameterOption($values, $onlyParams = false)
/**
* {@inheritdoc}
*/
- public function getParameterOption($values, $default = false, $onlyParams = false)
+ public function getParameterOption($values, $default = false, bool $onlyParams = false)
{
$values = (array) $values;
$tokens = $this->tokens;
diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php
index 25d2b750b4aae..66f0bedc3626b 100644
--- a/src/Symfony/Component/Console/Input/ArrayInput.php
+++ b/src/Symfony/Component/Console/Input/ArrayInput.php
@@ -53,7 +53,7 @@ public function getFirstArgument()
/**
* {@inheritdoc}
*/
- public function hasParameterOption($values, $onlyParams = false)
+ public function hasParameterOption($values, bool $onlyParams = false)
{
$values = (array) $values;
@@ -77,7 +77,7 @@ public function hasParameterOption($values, $onlyParams = false)
/**
* {@inheritdoc}
*/
- public function getParameterOption($values, $default = false, $onlyParams = false)
+ public function getParameterOption($values, $default = false, bool $onlyParams = false)
{
$values = (array) $values;
diff --git a/src/Symfony/Component/Console/Input/Input.php b/src/Symfony/Component/Console/Input/Input.php
index c1220316dcfff..ff5d3170f4e04 100644
--- a/src/Symfony/Component/Console/Input/Input.php
+++ b/src/Symfony/Component/Console/Input/Input.php
@@ -88,9 +88,9 @@ public function isInteractive()
/**
* {@inheritdoc}
*/
- public function setInteractive($interactive)
+ public function setInteractive(bool $interactive)
{
- $this->interactive = (bool) $interactive;
+ $this->interactive = $interactive;
}
/**
@@ -104,7 +104,7 @@ public function getArguments()
/**
* {@inheritdoc}
*/
- public function getArgument($name)
+ public function getArgument(string $name)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
@@ -116,7 +116,7 @@ public function getArgument($name)
/**
* {@inheritdoc}
*/
- public function setArgument($name, $value)
+ public function setArgument(string $name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
@@ -144,7 +144,7 @@ public function getOptions()
/**
* {@inheritdoc}
*/
- public function getOption($name)
+ public function getOption(string $name)
{
if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
@@ -156,7 +156,7 @@ public function getOption($name)
/**
* {@inheritdoc}
*/
- public function setOption($name, $value)
+ public function setOption(string $name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
@@ -168,7 +168,7 @@ public function setOption($name, $value)
/**
* {@inheritdoc}
*/
- public function hasOption($name)
+ public function hasOption(string $name)
{
return $this->definition->hasOption($name);
}
@@ -176,11 +176,9 @@ public function hasOption($name)
/**
* Escapes a token through escapeshellarg if it contains unsafe chars.
*
- * @param string $token
- *
* @return string
*/
- public function escapeToken($token)
+ public function escapeToken(string $token)
{
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}
diff --git a/src/Symfony/Component/Console/Input/InputDefinition.php b/src/Symfony/Component/Console/Input/InputDefinition.php
index 75a975213fe68..4e95e9b331b34 100644
--- a/src/Symfony/Component/Console/Input/InputDefinition.php
+++ b/src/Symfony/Component/Console/Input/InputDefinition.php
@@ -67,7 +67,7 @@ public function setDefinition(array $definition)
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
- public function setArguments($arguments = [])
+ public function setArguments(array $arguments = [])
{
$this->arguments = [];
$this->requiredCount = 0;
@@ -81,7 +81,7 @@ public function setArguments($arguments = [])
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
- public function addArguments($arguments = [])
+ public function addArguments(?array $arguments = [])
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
@@ -204,7 +204,7 @@ public function getArgumentDefaults()
*
* @param InputOption[] $options An array of InputOption objects
*/
- public function setOptions($options = [])
+ public function setOptions(array $options = [])
{
$this->options = [];
$this->shortcuts = [];
@@ -216,7 +216,7 @@ public function setOptions($options = [])
*
* @param InputOption[] $options An array of InputOption objects
*/
- public function addOptions($options = [])
+ public function addOptions(array $options = [])
{
foreach ($options as $option) {
$this->addOption($option);
@@ -251,13 +251,11 @@ public function addOption(InputOption $option)
/**
* Returns an InputOption by name.
*
- * @param string $name The InputOption name
- *
* @return InputOption A InputOption object
*
* @throws InvalidArgumentException When option given doesn't exist
*/
- public function getOption($name)
+ public function getOption(string $name)
{
if (!$this->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
@@ -272,11 +270,9 @@ public function getOption($name)
* This method can't be used to check if the user included the option when
* executing the command (use getOption() instead).
*
- * @param string $name The InputOption name
- *
* @return bool true if the InputOption object exists, false otherwise
*/
- public function hasOption($name)
+ public function hasOption(string $name)
{
return isset($this->options[$name]);
}
@@ -294,11 +290,9 @@ public function getOptions()
/**
* Returns true if an InputOption object exists by shortcut.
*
- * @param string $name The InputOption shortcut
- *
* @return bool true if the InputOption object exists, false otherwise
*/
- public function hasShortcut($name)
+ public function hasShortcut(string $name)
{
return isset($this->shortcuts[$name]);
}
@@ -306,11 +300,9 @@ public function hasShortcut($name)
/**
* Gets an InputOption by shortcut.
*
- * @param string $shortcut The Shortcut name
- *
* @return InputOption An InputOption object
*/
- public function getOptionForShortcut($shortcut)
+ public function getOptionForShortcut(string $shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
@@ -349,11 +341,9 @@ public function shortcutToName(string $shortcut): string
/**
* Gets the synopsis.
*
- * @param bool $short Whether to return the short version (with options folded) or not
- *
* @return string The synopsis
*/
- public function getSynopsis($short = false)
+ public function getSynopsis(bool $short = false)
{
$elements = [];
diff --git a/src/Symfony/Component/Console/Input/InputInterface.php b/src/Symfony/Component/Console/Input/InputInterface.php
index b9bcf3bbcd96d..f6ad722a0e350 100644
--- a/src/Symfony/Component/Console/Input/InputInterface.php
+++ b/src/Symfony/Component/Console/Input/InputInterface.php
@@ -41,7 +41,7 @@ public function getFirstArgument();
*
* @return bool true if the value is contained in the raw parameters
*/
- public function hasParameterOption($values, $onlyParams = false);
+ public function hasParameterOption($values, bool $onlyParams = false);
/**
* Returns the value of a raw option (not parsed).
@@ -57,7 +57,7 @@ public function hasParameterOption($values, $onlyParams = false);
*
* @return mixed The option value
*/
- public function getParameterOption($values, $default = false, $onlyParams = false);
+ public function getParameterOption($values, $default = false, bool $onlyParams = false);
/**
* Binds the current Input instance with the given arguments and options.
@@ -83,23 +83,20 @@ public function getArguments();
/**
* Returns the argument value for a given argument name.
*
- * @param string $name The argument name
- *
* @return string|string[]|null The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
- public function getArgument($name);
+ public function getArgument(string $name);
/**
* Sets an argument value by name.
*
- * @param string $name The argument name
* @param string|string[]|null $value The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
- public function setArgument($name, $value);
+ public function setArgument(string $name, $value);
/**
* Returns true if an InputArgument object exists by name or position.
@@ -120,32 +117,27 @@ public function getOptions();
/**
* Returns the option value for a given option name.
*
- * @param string $name The option name
- *
* @return string|string[]|bool|null The option value
*
* @throws InvalidArgumentException When option given doesn't exist
*/
- public function getOption($name);
+ public function getOption(string $name);
/**
* Sets an option value by name.
*
- * @param string $name The option name
* @param string|string[]|bool|null $value The option value
*
* @throws InvalidArgumentException When option given doesn't exist
*/
- public function setOption($name, $value);
+ public function setOption(string $name, $value);
/**
* Returns true if an InputOption object exists by name.
*
- * @param string $name The InputOption name
- *
* @return bool true if the InputOption object exists, false otherwise
*/
- public function hasOption($name);
+ public function hasOption(string $name);
/**
* Is this input means interactive?
@@ -156,8 +148,6 @@ public function isInteractive();
/**
* Sets the input interactivity.
- *
- * @param bool $interactive If the input should be interactive
*/
- public function setInteractive($interactive);
+ public function setInteractive(bool $interactive);
}
diff --git a/src/Symfony/Component/Console/Output/BufferedOutput.php b/src/Symfony/Component/Console/Output/BufferedOutput.php
index 8afc8931ed49c..a5ad7ada50001 100644
--- a/src/Symfony/Component/Console/Output/BufferedOutput.php
+++ b/src/Symfony/Component/Console/Output/BufferedOutput.php
@@ -34,7 +34,7 @@ public function fetch()
/**
* {@inheritdoc}
*/
- protected function doWrite($message, $newline)
+ protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;
diff --git a/src/Symfony/Component/Console/Output/ConsoleOutput.php b/src/Symfony/Component/Console/Output/ConsoleOutput.php
index 9684ad67bce4c..8356d4d55417d 100644
--- a/src/Symfony/Component/Console/Output/ConsoleOutput.php
+++ b/src/Symfony/Component/Console/Output/ConsoleOutput.php
@@ -60,7 +60,7 @@ public function section(): ConsoleSectionOutput
/**
* {@inheritdoc}
*/
- public function setDecorated($decorated)
+ public function setDecorated(bool $decorated)
{
parent::setDecorated($decorated);
$this->stderr->setDecorated($decorated);
@@ -78,7 +78,7 @@ public function setFormatter(OutputFormatterInterface $formatter)
/**
* {@inheritdoc}
*/
- public function setVerbosity($level)
+ public function setVerbosity(int $level)
{
parent::setVerbosity($level);
$this->stderr->setVerbosity($level);
diff --git a/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php b/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php
index f4c2fa623a426..6b6635f580ffa 100644
--- a/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php
+++ b/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php
@@ -16,8 +16,6 @@
* This adds information about stderr and section output stream.
*
* @author Dariusz Górecki
- *
- * @method ConsoleSectionOutput section() Creates a new output section
*/
interface ConsoleOutputInterface extends OutputInterface
{
@@ -29,4 +27,6 @@ interface ConsoleOutputInterface extends OutputInterface
public function getErrorOutput();
public function setErrorOutput(OutputInterface $error);
+
+ public function section(): ConsoleSectionOutput;
}
diff --git a/src/Symfony/Component/Console/Output/NullOutput.php b/src/Symfony/Component/Console/Output/NullOutput.php
index 218f285bfe51c..3bbe63ea0a007 100644
--- a/src/Symfony/Component/Console/Output/NullOutput.php
+++ b/src/Symfony/Component/Console/Output/NullOutput.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Console\Output;
-use Symfony\Component\Console\Formatter\OutputFormatter;
+use Symfony\Component\Console\Formatter\NullOutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
@@ -24,6 +24,8 @@
*/
class NullOutput implements OutputInterface
{
+ private $formatter;
+
/**
* {@inheritdoc}
*/
@@ -37,14 +39,17 @@ public function setFormatter(OutputFormatterInterface $formatter)
*/
public function getFormatter()
{
+ if ($this->formatter) {
+ return $this->formatter;
+ }
// to comply with the interface we must return a OutputFormatterInterface
- return new OutputFormatter();
+ return $this->formatter = new NullOutputFormatter();
}
/**
* {@inheritdoc}
*/
- public function setDecorated($decorated)
+ public function setDecorated(bool $decorated)
{
// do nothing
}
@@ -60,7 +65,7 @@ public function isDecorated()
/**
* {@inheritdoc}
*/
- public function setVerbosity($level)
+ public function setVerbosity(int $level)
{
// do nothing
}
@@ -108,7 +113,7 @@ public function isDebug()
/**
* {@inheritdoc}
*/
- public function writeln($messages, $options = self::OUTPUT_NORMAL)
+ public function writeln($messages, int $options = self::OUTPUT_NORMAL)
{
// do nothing
}
@@ -116,7 +121,7 @@ public function writeln($messages, $options = self::OUTPUT_NORMAL)
/**
* {@inheritdoc}
*/
- public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
+ public function write($messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
// do nothing
}
diff --git a/src/Symfony/Component/Console/Output/Output.php b/src/Symfony/Component/Console/Output/Output.php
index 9dd765113c026..ed13d58fc0d28 100644
--- a/src/Symfony/Component/Console/Output/Output.php
+++ b/src/Symfony/Component/Console/Output/Output.php
@@ -63,7 +63,7 @@ public function getFormatter()
/**
* {@inheritdoc}
*/
- public function setDecorated($decorated)
+ public function setDecorated(bool $decorated)
{
$this->formatter->setDecorated($decorated);
}
@@ -79,9 +79,9 @@ public function isDecorated()
/**
* {@inheritdoc}
*/
- public function setVerbosity($level)
+ public function setVerbosity(int $level)
{
- $this->verbosity = (int) $level;
+ $this->verbosity = $level;
}
/**
@@ -127,7 +127,7 @@ public function isDebug()
/**
* {@inheritdoc}
*/
- public function writeln($messages, $options = self::OUTPUT_NORMAL)
+ public function writeln($messages, int $options = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $options);
}
@@ -135,7 +135,7 @@ public function writeln($messages, $options = self::OUTPUT_NORMAL)
/**
* {@inheritdoc}
*/
- public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
+ public function write($messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
@@ -169,9 +169,6 @@ public function write($messages, $newline = false, $options = self::OUTPUT_NORMA
/**
* Writes a message to the output.
- *
- * @param string $message A message to write to the output
- * @param bool $newline Whether to add a newline or not
*/
- abstract protected function doWrite($message, $newline);
+ abstract protected function doWrite(string $message, bool $newline);
}
diff --git a/src/Symfony/Component/Console/Output/OutputInterface.php b/src/Symfony/Component/Console/Output/OutputInterface.php
index 0dfd12b98749c..38f82d869aa25 100644
--- a/src/Symfony/Component/Console/Output/OutputInterface.php
+++ b/src/Symfony/Component/Console/Output/OutputInterface.php
@@ -37,7 +37,7 @@ interface OutputInterface
* @param bool $newline Whether to add a newline
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*/
- public function write($messages, $newline = false, $options = 0);
+ public function write($messages, bool $newline = false, int $options = 0);
/**
* Writes a message to the output and adds a newline at the end.
@@ -45,14 +45,12 @@ public function write($messages, $newline = false, $options = 0);
* @param string|iterable $messages The message as an iterable of strings or a single string
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*/
- public function writeln($messages, $options = 0);
+ public function writeln($messages, int $options = 0);
/**
* Sets the verbosity of the output.
- *
- * @param int $level The level of verbosity (one of the VERBOSITY constants)
*/
- public function setVerbosity($level);
+ public function setVerbosity(int $level);
/**
* Gets the current verbosity of the output.
@@ -91,10 +89,8 @@ public function isDebug();
/**
* Sets the decorated flag.
- *
- * @param bool $decorated Whether to decorate the messages
*/
- public function setDecorated($decorated);
+ public function setDecorated(bool $decorated);
/**
* Gets the decorated flag.
diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php
index 312621086542e..0b24052e9f7c3 100644
--- a/src/Symfony/Component/Console/Output/StreamOutput.php
+++ b/src/Symfony/Component/Console/Output/StreamOutput.php
@@ -68,7 +68,7 @@ public function getStream()
/**
* {@inheritdoc}
*/
- protected function doWrite($message, $newline)
+ protected function doWrite(string $message, bool $newline)
{
if ($newline) {
$message .= PHP_EOL;
@@ -114,16 +114,6 @@ protected function hasColorSupport()
|| 'xterm' === getenv('TERM');
}
- if (\function_exists('stream_isatty')) {
- return @stream_isatty($this->stream);
- }
-
- if (\function_exists('posix_isatty')) {
- return @posix_isatty($this->stream);
- }
-
- $stat = @fstat($this->stream);
- // Check if formatted mode is S_IFCHR
- return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
+ return stream_isatty($this->stream);
}
}
diff --git a/src/Symfony/Component/Console/Question/ChoiceQuestion.php b/src/Symfony/Component/Console/Question/ChoiceQuestion.php
index a4b302db3e18f..020b733f132ac 100644
--- a/src/Symfony/Component/Console/Question/ChoiceQuestion.php
+++ b/src/Symfony/Component/Console/Question/ChoiceQuestion.php
@@ -58,11 +58,9 @@ public function getChoices()
*
* When multiselect is set to true, multiple choices can be answered.
*
- * @param bool $multiselect
- *
* @return $this
*/
- public function setMultiselect($multiselect)
+ public function setMultiselect(bool $multiselect)
{
$this->multiselect = $multiselect;
$this->setValidator($this->getDefaultValidator());
@@ -93,11 +91,9 @@ public function getPrompt()
/**
* Sets the prompt for choices.
*
- * @param string $prompt
- *
* @return $this
*/
- public function setPrompt($prompt)
+ public function setPrompt(string $prompt)
{
$this->prompt = $prompt;
@@ -109,11 +105,9 @@ public function setPrompt($prompt)
*
* The error message has a string placeholder (%s) for the invalid value.
*
- * @param string $errorMessage
- *
* @return $this
*/
- public function setErrorMessage($errorMessage)
+ public function setErrorMessage(string $errorMessage)
{
$this->errorMessage = $errorMessage;
$this->setValidator($this->getDefaultValidator());
diff --git a/src/Symfony/Component/Console/Question/Question.php b/src/Symfony/Component/Console/Question/Question.php
index c28e0bf55b8e7..8b0e4d989a900 100644
--- a/src/Symfony/Component/Console/Question/Question.php
+++ b/src/Symfony/Component/Console/Question/Question.php
@@ -130,14 +130,11 @@ public function getAutocompleterValues()
/**
* Sets values for the autocompleter.
*
- * @param iterable|null $values
- *
* @return $this
*
- * @throws InvalidArgumentException
* @throws LogicException
*/
- public function setAutocompleterValues($values)
+ public function setAutocompleterValues(?iterable $values)
{
if (\is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
@@ -150,10 +147,8 @@ public function setAutocompleterValues($values)
$callback = static function () use ($values, &$valueCache) {
return $valueCache ?? $valueCache = iterator_to_array($values, false);
};
- } elseif (null === $values) {
- $callback = null;
} else {
- throw new InvalidArgumentException('Autocompleter values can be either an array, "null" or a "Traversable" object.');
+ $callback = null;
}
return $this->setAutocompleterCallback($callback);
@@ -212,13 +207,11 @@ public function getValidator()
*
* Null means an unlimited number of attempts.
*
- * @param int|null $attempts
- *
* @return $this
*
* @throws InvalidArgumentException in case the number of attempts is invalid
*/
- public function setMaxAttempts($attempts)
+ public function setMaxAttempts(?int $attempts)
{
if (null !== $attempts && $attempts < 1) {
throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
@@ -267,7 +260,7 @@ public function getNormalizer()
return $this->normalizer;
}
- protected function isAssoc($array)
+ protected function isAssoc(array $array)
{
return (bool) \count(array_filter(array_keys($array), 'is_string'));
}
diff --git a/src/Symfony/Component/Console/SingleCommandApplication.php b/src/Symfony/Component/Console/SingleCommandApplication.php
new file mode 100644
index 0000000000000..ffa176fbd0bc8
--- /dev/null
+++ b/src/Symfony/Component/Console/SingleCommandApplication.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Grégoire Pineau
+ */
+class SingleCommandApplication extends Command
+{
+ private $version = 'UNKNOWN';
+ private $running = false;
+
+ public function setVersion(string $version): self
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+
+ public function run(InputInterface $input = null, OutputInterface $output = null): int
+ {
+ if ($this->running) {
+ return parent::run($input, $output);
+ }
+
+ // We use the command name as the application name
+ $application = new Application($this->getName() ?: 'UNKNOWN', $this->version);
+ // Fix the usage of the command displayed with "--help"
+ $this->setName($_SERVER['argv'][0]);
+ $application->add($this);
+ $application->setDefaultCommand($this->getName(), true);
+
+ $this->running = true;
+ try {
+ $ret = $application->run($input, $output);
+ } finally {
+ $this->running = false;
+ }
+
+ return $ret ?? 1;
+ }
+}
diff --git a/src/Symfony/Component/Console/Style/OutputStyle.php b/src/Symfony/Component/Console/Style/OutputStyle.php
index b1262b55de4e9..d46947f053c4a 100644
--- a/src/Symfony/Component/Console/Style/OutputStyle.php
+++ b/src/Symfony/Component/Console/Style/OutputStyle.php
@@ -33,17 +33,15 @@ public function __construct(OutputInterface $output)
/**
* {@inheritdoc}
*/
- public function newLine($count = 1)
+ public function newLine(int $count = 1)
{
$this->output->write(str_repeat(PHP_EOL, $count));
}
/**
- * @param int $max
- *
* @return ProgressBar
*/
- public function createProgressBar($max = 0)
+ public function createProgressBar(int $max = 0)
{
return new ProgressBar($this->output, $max);
}
@@ -51,7 +49,7 @@ public function createProgressBar($max = 0)
/**
* {@inheritdoc}
*/
- public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
+ public function write($messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
$this->output->write($messages, $newline, $type);
}
@@ -59,7 +57,7 @@ public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
/**
* {@inheritdoc}
*/
- public function writeln($messages, $type = self::OUTPUT_NORMAL)
+ public function writeln($messages, int $type = self::OUTPUT_NORMAL)
{
$this->output->writeln($messages, $type);
}
@@ -67,7 +65,7 @@ public function writeln($messages, $type = self::OUTPUT_NORMAL)
/**
* {@inheritdoc}
*/
- public function setVerbosity($level)
+ public function setVerbosity(int $level)
{
$this->output->setVerbosity($level);
}
@@ -83,7 +81,7 @@ public function getVerbosity()
/**
* {@inheritdoc}
*/
- public function setDecorated($decorated)
+ public function setDecorated(bool $decorated)
{
$this->output->setDecorated($decorated);
}
diff --git a/src/Symfony/Component/Console/Style/StyleInterface.php b/src/Symfony/Component/Console/Style/StyleInterface.php
index 3b5b8af516106..afb841c0d0890 100644
--- a/src/Symfony/Component/Console/Style/StyleInterface.php
+++ b/src/Symfony/Component/Console/Style/StyleInterface.php
@@ -20,17 +20,13 @@ interface StyleInterface
{
/**
* Formats a command title.
- *
- * @param string $message
*/
- public function title($message);
+ public function title(string $message);
/**
* Formats a section title.
- *
- * @param string $message
*/
- public function section($message);
+ public function section(string $message);
/**
* Formats a list.
@@ -87,64 +83,47 @@ public function table(array $headers, array $rows);
/**
* Asks a question.
*
- * @param string $question
- * @param string|null $default
- * @param callable|null $validator
- *
* @return mixed
*/
- public function ask($question, $default = null, $validator = null);
+ public function ask(string $question, ?string $default = null, callable $validator = null);
/**
* Asks a question with the user input hidden.
*
- * @param string $question
- * @param callable|null $validator
- *
* @return mixed
*/
- public function askHidden($question, $validator = null);
+ public function askHidden(string $question, callable $validator = null);
/**
* Asks for confirmation.
*
- * @param string $question
- * @param bool $default
- *
* @return bool
*/
- public function confirm($question, $default = true);
+ public function confirm(string $question, bool $default = true);
/**
* Asks a choice question.
*
- * @param string $question
* @param string|int|null $default
*
* @return mixed
*/
- public function choice($question, array $choices, $default = null);
+ public function choice(string $question, array $choices, $default = null);
/**
* Add newline(s).
- *
- * @param int $count The number of newlines
*/
- public function newLine($count = 1);
+ public function newLine(int $count = 1);
/**
* Starts the progress output.
- *
- * @param int $max Maximum steps (0 if unknown)
*/
- public function progressStart($max = 0);
+ public function progressStart(int $max = 0);
/**
* Advances the progress output X steps.
- *
- * @param int $step Number of steps to advance
*/
- public function progressAdvance($step = 1);
+ public function progressAdvance(int $step = 1);
/**
* Finishes the progress output.
diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php
index 5efe2d4b142ed..271752f1444e6 100644
--- a/src/Symfony/Component/Console/Style/SymfonyStyle.php
+++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php
@@ -58,13 +58,8 @@ public function __construct(InputInterface $input, OutputInterface $output)
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
- * @param string|null $type The block type (added in [] on first line)
- * @param string|null $style The style to apply to the whole block
- * @param string $prefix The prefix for the block
- * @param bool $padding Whether to add vertical padding
- * @param bool $escape Whether to escape the message
*/
- public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = true)
+ public function block($messages, ?string $type = null, ?string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true)
{
$messages = \is_array($messages) ? array_values($messages) : [$messages];
@@ -76,7 +71,7 @@ public function block($messages, $type = null, $style = null, $prefix = ' ', $pa
/**
* {@inheritdoc}
*/
- public function title($message)
+ public function title(string $message)
{
$this->autoPrependBlock();
$this->writeln([
@@ -89,7 +84,7 @@ public function title($message)
/**
* {@inheritdoc}
*/
- public function section($message)
+ public function section(string $message)
{
$this->autoPrependBlock();
$this->writeln([
@@ -259,7 +254,7 @@ public function definitionList(...$list)
/**
* {@inheritdoc}
*/
- public function ask($question, $default = null, $validator = null)
+ public function ask(string $question, ?string $default = null, $validator = null)
{
$question = new Question($question, $default);
$question->setValidator($validator);
@@ -270,7 +265,7 @@ public function ask($question, $default = null, $validator = null)
/**
* {@inheritdoc}
*/
- public function askHidden($question, $validator = null)
+ public function askHidden(string $question, $validator = null)
{
$question = new Question($question);
@@ -291,7 +286,7 @@ public function confirm($question, $default = true)
/**
* {@inheritdoc}
*/
- public function choice($question, array $choices, $default = null)
+ public function choice(string $question, array $choices, $default = null)
{
if (null !== $default) {
$values = array_flip($choices);
@@ -304,7 +299,7 @@ public function choice($question, array $choices, $default = null)
/**
* {@inheritdoc}
*/
- public function progressStart($max = 0)
+ public function progressStart(int $max = 0)
{
$this->progressBar = $this->createProgressBar($max);
$this->progressBar->start();
@@ -313,7 +308,7 @@ public function progressStart($max = 0)
/**
* {@inheritdoc}
*/
- public function progressAdvance($step = 1)
+ public function progressAdvance(int $step = 1)
{
$this->getProgressBar()->advance($step);
}
@@ -331,7 +326,7 @@ public function progressFinish()
/**
* {@inheritdoc}
*/
- public function createProgressBar($max = 0)
+ public function createProgressBar(int $max = 0)
{
$progressBar = parent::createProgressBar($max);
@@ -370,7 +365,7 @@ public function askQuestion(Question $question)
/**
* {@inheritdoc}
*/
- public function writeln($messages, $type = self::OUTPUT_NORMAL)
+ public function writeln($messages, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
@@ -385,7 +380,7 @@ public function writeln($messages, $type = self::OUTPUT_NORMAL)
/**
* {@inheritdoc}
*/
- public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
+ public function write($messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
@@ -400,7 +395,7 @@ public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
/**
* {@inheritdoc}
*/
- public function newLine($count = 1)
+ public function newLine(int $count = 1)
{
parent::newLine($count);
$this->bufferedOutput->write(str_repeat("\n", $count));
diff --git a/src/Symfony/Component/Console/Tester/ApplicationTester.php b/src/Symfony/Component/Console/Tester/ApplicationTester.php
index 4f99da18d5f8b..d021c14358f2d 100644
--- a/src/Symfony/Component/Console/Tester/ApplicationTester.php
+++ b/src/Symfony/Component/Console/Tester/ApplicationTester.php
@@ -47,12 +47,9 @@ public function __construct(Application $application)
* * verbosity: Sets the output verbosity flag
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
*
- * @param array $input An array of arguments and options
- * @param array $options An array of options
- *
* @return int The command exit code
*/
- public function run(array $input, $options = [])
+ public function run(array $input, array $options = [])
{
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
diff --git a/src/Symfony/Component/Console/Tester/TesterTrait.php b/src/Symfony/Component/Console/Tester/TesterTrait.php
index a5c2088ae394e..73ee0103f917a 100644
--- a/src/Symfony/Component/Console/Tester/TesterTrait.php
+++ b/src/Symfony/Component/Console/Tester/TesterTrait.php
@@ -29,11 +29,9 @@ trait TesterTrait
/**
* Gets the display returned by the last execution of the command or application.
*
- * @param bool $normalize Whether to normalize end of lines to \n or not
- *
* @return string The display
*/
- public function getDisplay($normalize = false)
+ public function getDisplay(bool $normalize = false)
{
if (null === $this->output) {
throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?');
@@ -57,7 +55,7 @@ public function getDisplay($normalize = false)
*
* @return string
*/
- public function getErrorOutput($normalize = false)
+ public function getErrorOutput(bool $normalize = false)
{
if (!$this->captureStreamsIndependently) {
throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.');
diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php
index cc68f596eea9f..cd8904ea72203 100644
--- a/src/Symfony/Component/Console/Tests/ApplicationTest.php
+++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php
@@ -731,28 +731,14 @@ public function testFindHiddenWithExactName()
$this->assertInstanceOf('FooHiddenCommand', $application->find('afoohidden'));
}
- /**
- * @group legacy
- * @expectedDeprecation Command "%s:hidden" is hidden, finding it using an abbreviation is deprecated since Symfony 4.4, use its full name instead.
- * @dataProvider provideAbbreviationsForHiddenCommands
- */
- public function testFindHiddenWithAbbreviatedName($name)
+ public function testFindAmbiguousCommandsIfAllAlternativesAreHidden()
{
$application = new Application();
+ $application->add(new \FooCommand());
$application->add(new \FooHiddenCommand());
- $application->add(new \BarHiddenCommand());
-
- $application->find($name);
- }
- public function provideAbbreviationsForHiddenCommands()
- {
- return [
- ['foo:hidde'],
- ['afoohidd'],
- ['bar:hidde'],
- ];
+ $this->assertInstanceOf('FooCommand', $application->find('foo:'));
}
public function testSetCatchExceptions()
diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php
index c51a47c6853dc..bbfd228ac7517 100644
--- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php
+++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php
@@ -179,13 +179,6 @@ public function testGetSetAliases()
$this->assertEquals(['name1'], $command->getAliases(), '->setAliases() sets the aliases');
}
- public function testSetAliasesNull()
- {
- $command = new \TestCommand();
- $this->expectException('InvalidArgumentException');
- $command->setAliases(null);
- }
-
public function testGetSynopsis()
{
$command = new \TestCommand();
diff --git a/src/Symfony/Component/Console/Tests/Formatter/NullOutputFormatterStyleTest.php b/src/Symfony/Component/Console/Tests/Formatter/NullOutputFormatterStyleTest.php
new file mode 100644
index 0000000000000..616e7f71416bc
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Formatter/NullOutputFormatterStyleTest.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Tests\Output;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Formatter\NullOutputFormatterStyle;
+
+/**
+ * @author Tien Xuan Vo
+ */
+class NullOutputFormatterStyleTest extends TestCase
+{
+ public function testApply()
+ {
+ $style = new NullOutputFormatterStyle();
+
+ $this->assertSame('foo', $style->apply('foo'));
+ }
+
+ public function testSetForeground()
+ {
+ $style = new NullOutputFormatterStyle();
+ $style->setForeground('black');
+ $this->assertSame('foo', $style->apply('foo'));
+ }
+
+ public function testSetBackground()
+ {
+ $style = new NullOutputFormatterStyle();
+ $style->setBackground('blue');
+ $this->assertSame('foo', $style->apply('foo'));
+ }
+
+ public function testOptions()
+ {
+ $style = new NullOutputFormatterStyle();
+
+ $style->setOptions(['reverse', 'conceal']);
+ $this->assertSame('foo', $style->apply('foo'));
+
+ $style->setOption('bold');
+ $this->assertSame('foo', $style->apply('foo'));
+
+ $style->unsetOption('reverse');
+ $this->assertSame('foo', $style->apply('foo'));
+ }
+}
diff --git a/src/Symfony/Component/Console/Tests/Formatter/NullOutputFormatterTest.php b/src/Symfony/Component/Console/Tests/Formatter/NullOutputFormatterTest.php
new file mode 100644
index 0000000000000..a717cf3d51953
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Formatter/NullOutputFormatterTest.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Tests\Output;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Formatter\NullOutputFormatter;
+use Symfony\Component\Console\Formatter\NullOutputFormatterStyle;
+use Symfony\Component\Console\Formatter\OutputFormatterStyle;
+
+/**
+ * @author Tien Xuan Vo
+ */
+class NullOutputFormatterTest extends TestCase
+{
+ public function testFormat()
+ {
+ $formatter = new NullOutputFormatter();
+
+ $message = 'this message will not be changed';
+ $formatter->format($message);
+
+ $this->assertSame('this message will not be changed', $message);
+ }
+
+ public function testGetStyle()
+ {
+ $formatter = new NullOutputFormatter();
+ $this->assertInstanceof(NullOutputFormatterStyle::class, $style = $formatter->getStyle('null'));
+ $this->assertSame($style, $formatter->getStyle('null'));
+ }
+
+ public function testSetStyle()
+ {
+ $formatter = new NullOutputFormatter();
+ $style = new OutputFormatterStyle();
+ $formatter->setStyle('null', $style);
+ $this->assertNotSame($style, $formatter->getStyle('null'));
+ }
+
+ public function testHasStyle()
+ {
+ $formatter = new NullOutputFormatter();
+ $this->assertFalse($formatter->hasStyle('null'));
+ }
+
+ public function testIsDecorated()
+ {
+ $formatter = new NullOutputFormatter();
+ $this->assertFalse($formatter->isDecorated());
+ }
+
+ public function testSetDecorated()
+ {
+ $formatter = new NullOutputFormatter();
+ $formatter->setDecorated(true);
+ $this->assertFalse($formatter->isDecorated());
+ }
+}
diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
index fcba3b3b2fd19..19d0c9d5d7256 100644
--- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
@@ -797,6 +797,25 @@ public function testTraversableMultiselectAutocomplete()
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
+ public function testAutocompleteMoveCursorBackwards()
+ {
+ // F
+ $inputStream = $this->getInputStream("F\t\177\177\177");
+
+ $dialog = new QuestionHelper();
+ $helperSet = new HelperSet([new FormatterHelper()]);
+ $dialog->setHelperSet($helperSet);
+
+ $question = new Question('Question?', 'F⭐Y');
+ $question->setAutocompleterValues(['F⭐Y']);
+
+ $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question);
+
+ $stream = $output->getStream();
+ rewind($stream);
+ $this->assertStringEndsWith("\033[1D\033[K\033[2D\033[K\033[1D\033[K", stream_get_contents($stream));
+ }
+
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
diff --git a/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php b/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php
index 5980192540c94..4aa2ac8781a04 100644
--- a/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php
@@ -21,6 +21,6 @@ public function testSetPadTypeWithInvalidType()
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
$style = new TableStyle();
- $style->setPadType('TEST');
+ $style->setPadType(31);
}
}
diff --git a/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php b/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php
index b7ff4be312ea3..1e0967ea5e6da 100644
--- a/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php
+++ b/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Tests\Output;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Formatter\NullOutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\Output;
@@ -40,6 +41,13 @@ public function testVerbosity()
$this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput');
}
+ public function testGetFormatter()
+ {
+ $output = new NullOutput();
+ $this->assertInstanceof(NullOutputFormatter::class, $formatter = $output->getFormatter());
+ $this->assertSame($formatter, $output->getFormatter());
+ }
+
public function testSetFormatter()
{
$output = new NullOutput();
diff --git a/src/Symfony/Component/Console/Tests/Output/OutputTest.php b/src/Symfony/Component/Console/Tests/Output/OutputTest.php
index 7cfa6cdc0aab6..330064973baa0 100644
--- a/src/Symfony/Component/Console/Tests/Output/OutputTest.php
+++ b/src/Symfony/Component/Console/Tests/Output/OutputTest.php
@@ -182,7 +182,7 @@ public function clear()
$this->output = '';
}
- protected function doWrite($message, $newline)
+ protected function doWrite(string $message, bool $newline)
{
$this->output .= $message.($newline ? "\n" : '');
}
diff --git a/src/Symfony/Component/Console/Tests/Question/QuestionTest.php b/src/Symfony/Component/Console/Tests/Question/QuestionTest.php
index 5da76062c61ee..357fe4d77eea1 100644
--- a/src/Symfony/Component/Console/Tests/Question/QuestionTest.php
+++ b/src/Symfony/Component/Console/Tests/Question/QuestionTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Console\Tests\Question;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Question\Question;
class QuestionTest extends TestCase
@@ -150,10 +149,7 @@ public function providerSetAutocompleterValuesInvalid()
*/
public function testSetAutocompleterValuesInvalid($values)
{
- self::expectException(InvalidArgumentException::class);
- self::expectExceptionMessage(
- 'Autocompleter values can be either an array, "null" or a "Traversable" object.'
- );
+ self::expectException(\TypeError::class);
$this->question->setAutocompleterValues($values);
}
@@ -271,7 +267,7 @@ public function testGetSetMaxAttempts($attempts)
public function providerSetMaxAttemptsInvalid()
{
- return [['Potato'], [0], [-1]];
+ return [[0], [-1]];
}
/**
diff --git a/src/Symfony/Component/Console/Tests/phpt/single_application/arg.phpt b/src/Symfony/Component/Console/Tests/phpt/single_application/arg.phpt
new file mode 100644
index 0000000000000..049776dc87f68
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/phpt/single_application/arg.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Single Application can be executed
+--ARGS--
+You
+--FILE--
+addArgument('who', InputArgument::OPTIONAL, 'Who', 'World')
+ ->setCode(function (InputInterface $input, OutputInterface $output): int {
+ $output->writeln(sprintf('Hello %s!', $input->getArgument('who')));
+
+ return 0;
+ })
+ ->run()
+;
+?>
+--EXPECT--
+Hello You!
diff --git a/src/Symfony/Component/Console/Tests/phpt/single_application/default.phpt b/src/Symfony/Component/Console/Tests/phpt/single_application/default.phpt
new file mode 100644
index 0000000000000..bb0387ea43217
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/phpt/single_application/default.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Single Application can be executed
+--FILE--
+setCode(function (InputInterface $input, OutputInterface $output): int {
+ $output->writeln('Hello World!');
+
+ return 0;
+ })
+ ->run()
+;
+?>
+--EXPECT--
+Hello World!
diff --git a/src/Symfony/Component/Console/Tests/phpt/single_application/help_name.phpt b/src/Symfony/Component/Console/Tests/phpt/single_application/help_name.phpt
new file mode 100644
index 0000000000000..1ade3188b14db
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/phpt/single_application/help_name.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Single Application can be executed
+--ARGS--
+--help --no-ansi
+--FILE--
+setName('My Super Command')
+ ->setCode(function (InputInterface $input, OutputInterface $output): int {
+ return 0;
+ })
+ ->run()
+;
+?>
+--EXPECTF--
+Usage:
+ %s
+
+Options:
+ -h, --help Display this help message
+ -q, --quiet Do not output any message
+ -V, --version Display this application version
+ --ansi Force ANSI output
+ --no-ansi Disable ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
diff --git a/src/Symfony/Component/Console/Tests/phpt/single_application/version_default_name.phpt b/src/Symfony/Component/Console/Tests/phpt/single_application/version_default_name.phpt
new file mode 100644
index 0000000000000..3e40fa3b84dd9
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/phpt/single_application/version_default_name.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Single Application can be executed
+--ARGS--
+--version --no-ansi
+--FILE--
+setName('My Super Command')
+ ->setVersion('1.0.0')
+ ->setCode(function (InputInterface $input, OutputInterface $output): int {
+ return 0;
+ })
+ ->run()
+;
+?>
+--EXPECT--
+My Super Command 1.0.0
diff --git a/src/Symfony/Component/Console/Tests/phpt/single_application/version_name.phpt b/src/Symfony/Component/Console/Tests/phpt/single_application/version_name.phpt
new file mode 100644
index 0000000000000..4f1b7395defd4
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/phpt/single_application/version_name.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Single Application can be executed
+--ARGS--
+--version
+--FILE--
+setCode(function (InputInterface $input, OutputInterface $output): int {
+ return 0;
+ })
+ ->run()
+;
+?>
+--EXPECT--
+Console Tool
diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json
index 506539d1b55b5..b92d62554db51 100644
--- a/src/Symfony/Component/Console/composer.json
+++ b/src/Symfony/Component/Console/composer.json
@@ -16,18 +16,19 @@
}
],
"require": {
- "php": "^7.1.3",
+ "php": "^7.2.5",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php73": "^1.8",
- "symfony/service-contracts": "^1.1|^2"
+ "symfony/service-contracts": "^1.1|^2",
+ "symfony/string": "^5.1"
},
"require-dev": {
- "symfony/config": "^3.4|^4.0|^5.0",
- "symfony/event-dispatcher": "^4.3",
- "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+ "symfony/config": "^4.4|^5.0",
+ "symfony/event-dispatcher": "^4.4|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
"symfony/lock": "^4.4|^5.0",
- "symfony/process": "^3.4|^4.0|^5.0",
- "symfony/var-dumper": "^4.3|^5.0",
+ "symfony/process": "^4.4|^5.0",
+ "symfony/var-dumper": "^4.4|^5.0",
"psr/log": "~1.0"
},
"provide": {
@@ -40,10 +41,11 @@
"psr/log": "For using the console logger"
},
"conflict": {
- "symfony/dependency-injection": "<3.4",
- "symfony/event-dispatcher": "<4.3|>=5",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/dotenv": "<5.1",
+ "symfony/event-dispatcher": "<4.4",
"symfony/lock": "<4.4",
- "symfony/process": "<3.3"
+ "symfony/process": "<4.4"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Console\\": "" },
@@ -54,7 +56,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/CssSelector/CssSelectorConverter.php b/src/Symfony/Component/CssSelector/CssSelectorConverter.php
index d1aeb7eb1702e..bbb6afe2172fc 100644
--- a/src/Symfony/Component/CssSelector/CssSelectorConverter.php
+++ b/src/Symfony/Component/CssSelector/CssSelectorConverter.php
@@ -27,6 +27,10 @@
class CssSelectorConverter
{
private $translator;
+ private $cache;
+
+ private static $xmlCache = [];
+ private static $htmlCache = [];
/**
* @param bool $html Whether HTML support should be enabled. Disable it for XML documents
@@ -37,6 +41,9 @@ public function __construct(bool $html = true)
if ($html) {
$this->translator->registerExtension(new HtmlExtension($this->translator));
+ $this->cache = &self::$htmlCache;
+ } else {
+ $this->cache = &self::$xmlCache;
}
$this->translator
@@ -53,13 +60,10 @@ public function __construct(bool $html = true)
* Optionally, a prefix can be added to the resulting XPath
* expression with the $prefix parameter.
*
- * @param string $cssExpr The CSS expression
- * @param string $prefix An optional prefix for the XPath expression
- *
* @return string
*/
- public function toXPath($cssExpr, $prefix = 'descendant-or-self::')
+ public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::')
{
- return $this->translator->cssToXPath($cssExpr, $prefix);
+ return $this->cache[$prefix][$cssExpr] ?? $this->cache[$prefix][$cssExpr] = $this->translator->cssToXPath($cssExpr, $prefix);
}
}
diff --git a/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php b/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php
index 1200c979ea6ac..7deacf9c5ee19 100644
--- a/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php
+++ b/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php
@@ -24,32 +24,25 @@
class SyntaxErrorException extends ParseException
{
/**
- * @param string $expectedValue
- *
* @return self
*/
- public static function unexpectedToken($expectedValue, Token $foundToken)
+ public static function unexpectedToken(string $expectedValue, Token $foundToken)
{
return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken));
}
/**
- * @param string $pseudoElement
- * @param string $unexpectedLocation
- *
* @return self
*/
- public static function pseudoElementFound($pseudoElement, $unexpectedLocation)
+ public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation)
{
return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation));
}
/**
- * @param int $position
- *
* @return self
*/
- public static function unclosedString($position)
+ public static function unclosedString(int $position)
{
return new self(sprintf('Unclosed/invalid string at %s.', $position));
}
diff --git a/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php b/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php
index 82e527c62e78b..420c33087d4c4 100644
--- a/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php
+++ b/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php
@@ -26,6 +26,10 @@ public function testCssToXPath()
$this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", $converter->toXPath('h1.foo'));
$this->assertEquals('descendant-or-self::foo:h1', $converter->toXPath('foo|h1'));
$this->assertEquals('descendant-or-self::h1', $converter->toXPath('H1'));
+
+ // Test the cache layer
+ $converter = new CssSelectorConverter();
+ $this->assertEquals('descendant-or-self::h1', $converter->toXPath('H1'));
}
public function testCssToXPathXml()
@@ -33,6 +37,10 @@ public function testCssToXPathXml()
$converter = new CssSelectorConverter(false);
$this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1'));
+
+ $converter = new CssSelectorConverter(false);
+ // Test the cache layer
+ $this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1'));
}
public function testParseExceptions()
diff --git a/src/Symfony/Component/CssSelector/composer.json b/src/Symfony/Component/CssSelector/composer.json
index 65642dc9bc584..e41cf7496519b 100644
--- a/src/Symfony/Component/CssSelector/composer.json
+++ b/src/Symfony/Component/CssSelector/composer.json
@@ -20,7 +20,7 @@
}
],
"require": {
- "php": "^7.1.3"
+ "php": "^7.2.5"
},
"autoload": {
"psr-4": { "Symfony\\Component\\CssSelector\\": "" },
@@ -31,7 +31,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Debug/BufferingLogger.php b/src/Symfony/Component/Debug/BufferingLogger.php
deleted file mode 100644
index 7025050fa2772..0000000000000
--- a/src/Symfony/Component/Debug/BufferingLogger.php
+++ /dev/null
@@ -1,44 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-use Psr\Log\AbstractLogger;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', BufferingLogger::class, \Symfony\Component\ErrorHandler\BufferingLogger::class), E_USER_DEPRECATED);
-
-/**
- * A buffering logger that stacks logs for later.
- *
- * @author Nicolas Grekas
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\BufferingLogger instead.
- */
-class BufferingLogger extends AbstractLogger
-{
- private $logs = [];
-
- /**
- * @return void
- */
- public function log($level, $message, array $context = [])
- {
- $this->logs[] = [$level, $message, $context];
- }
-
- public function cleanLogs()
- {
- $logs = $this->logs;
- $this->logs = [];
-
- return $logs;
- }
-}
diff --git a/src/Symfony/Component/Debug/CHANGELOG.md b/src/Symfony/Component/Debug/CHANGELOG.md
deleted file mode 100644
index 989c1a72da7af..0000000000000
--- a/src/Symfony/Component/Debug/CHANGELOG.md
+++ /dev/null
@@ -1,84 +0,0 @@
-CHANGELOG
-=========
-
-4.4.0
------
-
- * deprecated `FlattenException`, use the `FlattenException` of the `ErrorHandler` component
- * deprecated the whole component in favor of the `ErrorHandler` component
-
-4.3.0
------
-
-* made the `ErrorHandler` and `ExceptionHandler` classes final
-* added `Exception\FlattenException::getAsString` and
-`Exception\FlattenException::getTraceAsString` to increase compatibility to php
-exception objects
-
-4.0.0
------
-
-* removed the symfony_debug extension
-* removed `ContextErrorException`
-
-3.4.0
------
-
-* deprecated `ErrorHandler::stackErrors()` and `ErrorHandler::unstackErrors()`
-
-3.3.0
------
-
-* deprecated the `ContextErrorException` class: use \ErrorException directly now
-
-3.2.0
------
-
-* `FlattenException::getTrace()` now returns additional type descriptions
- `integer` and `float`.
-
-
-3.0.0
------
-
-* removed classes, methods and interfaces deprecated in 2.x
-
-2.8.0
------
-
-* added BufferingLogger for errors that happen before a proper logger is configured
-* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);`
-* deprecate ExceptionHandler::createResponse
-
-2.7.0
------
-
-* added deprecations checking for parent interfaces/classes to DebugClassLoader
-* added ZTS support to symfony_debug extension
-* added symfony_debug_backtrace() to symfony_debug extension
- to track the backtrace of fatal errors
-
-2.6.0
------
-
-* generalized ErrorHandler and ExceptionHandler,
- with some new methods and others deprecated
-* enhanced error messages for uncaught exceptions
-
-2.5.0
------
-
-* added ExceptionHandler::setHandler()
-* added UndefinedMethodFatalErrorHandler
-* deprecated DummyException
-
-2.4.0
------
-
- * added a DebugClassLoader able to wrap any autoloader providing a findFile method
- * improved error messages for not found classes and functions
-
-2.3.0
------
-
- * added the component
diff --git a/src/Symfony/Component/Debug/Debug.php b/src/Symfony/Component/Debug/Debug.php
deleted file mode 100644
index 788ad7d9243ff..0000000000000
--- a/src/Symfony/Component/Debug/Debug.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', Debug::class, \Symfony\Component\ErrorHandler\Debug::class), E_USER_DEPRECATED);
-
-/**
- * Registers all the debug tools.
- *
- * @author Fabien Potencier
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Debug instead.
- */
-class Debug
-{
- private static $enabled = false;
-
- /**
- * Enables the debug tools.
- *
- * This method registers an error handler and an exception handler.
- *
- * @param int $errorReportingLevel The level of error reporting you want
- * @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
- */
- public static function enable($errorReportingLevel = E_ALL, $displayErrors = true)
- {
- if (static::$enabled) {
- return;
- }
-
- static::$enabled = true;
-
- if (null !== $errorReportingLevel) {
- error_reporting($errorReportingLevel);
- } else {
- error_reporting(E_ALL);
- }
-
- if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
- ini_set('display_errors', 0);
- ExceptionHandler::register();
- } elseif ($displayErrors && (!filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) || ini_get('error_log'))) {
- // CLI - display errors only if they're not already logged to STDERR
- ini_set('display_errors', 1);
- }
- if ($displayErrors) {
- ErrorHandler::register(new ErrorHandler(new BufferingLogger()));
- } else {
- ErrorHandler::register()->throwAt(0, true);
- }
-
- DebugClassLoader::enable();
- }
-}
diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php
deleted file mode 100644
index 3ff450b70058e..0000000000000
--- a/src/Symfony/Component/Debug/DebugClassLoader.php
+++ /dev/null
@@ -1,538 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', DebugClassLoader::class, \Symfony\Component\ErrorHandler\DebugClassLoader::class), E_USER_DEPRECATED);
-
-/**
- * Autoloader checking if the class is really defined in the file found.
- *
- * The ClassLoader will wrap all registered autoloaders
- * and will throw an exception if a file is found but does
- * not declare the class.
- *
- * @author Fabien Potencier
- * @author Christophe Coevoet
- * @author Nicolas Grekas
- * @author Guilhem Niot
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\DebugClassLoader instead.
- */
-class DebugClassLoader
-{
- private $classLoader;
- private $isFinder;
- private $loaded = [];
- private static $caseCheck;
- private static $checkedClasses = [];
- private static $final = [];
- private static $finalMethods = [];
- private static $deprecated = [];
- private static $internal = [];
- private static $internalMethods = [];
- private static $annotatedParameters = [];
- private static $darwinCache = ['/' => ['/', []]];
- private static $method = [];
-
- public function __construct(callable $classLoader)
- {
- $this->classLoader = $classLoader;
- $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile');
-
- if (!isset(self::$caseCheck)) {
- $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR);
- $i = strrpos($file, \DIRECTORY_SEPARATOR);
- $dir = substr($file, 0, 1 + $i);
- $file = substr($file, 1 + $i);
- $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file);
- $test = realpath($dir.$test);
-
- if (false === $test || false === $i) {
- // filesystem is case sensitive
- self::$caseCheck = 0;
- } elseif (substr($test, -\strlen($file)) === $file) {
- // filesystem is case insensitive and realpath() normalizes the case of characters
- self::$caseCheck = 1;
- } elseif (false !== stripos(PHP_OS, 'darwin')) {
- // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
- self::$caseCheck = 2;
- } else {
- // filesystem case checks failed, fallback to disabling them
- self::$caseCheck = 0;
- }
- }
- }
-
- /**
- * Gets the wrapped class loader.
- *
- * @return callable The wrapped class loader
- */
- public function getClassLoader()
- {
- return $this->classLoader;
- }
-
- /**
- * Wraps all autoloaders.
- */
- public static function enable()
- {
- // Ensures we don't hit https://bugs.php.net/42098
- class_exists('Symfony\Component\Debug\ErrorHandler');
- class_exists('Psr\Log\LogLevel');
-
- if (!\is_array($functions = spl_autoload_functions())) {
- return;
- }
-
- foreach ($functions as $function) {
- spl_autoload_unregister($function);
- }
-
- foreach ($functions as $function) {
- if (!\is_array($function) || !$function[0] instanceof self) {
- $function = [new static($function), 'loadClass'];
- }
-
- spl_autoload_register($function);
- }
- }
-
- /**
- * Disables the wrapping.
- */
- public static function disable()
- {
- if (!\is_array($functions = spl_autoload_functions())) {
- return;
- }
-
- foreach ($functions as $function) {
- spl_autoload_unregister($function);
- }
-
- foreach ($functions as $function) {
- if (\is_array($function) && $function[0] instanceof self) {
- $function = $function[0]->getClassLoader();
- }
-
- spl_autoload_register($function);
- }
- }
-
- /**
- * @return string|null
- */
- public function findFile($class)
- {
- return $this->isFinder ? $this->classLoader[0]->findFile($class) ?: null : null;
- }
-
- /**
- * Loads the given class or interface.
- *
- * @param string $class The name of the class
- *
- * @throws \RuntimeException
- */
- public function loadClass($class)
- {
- $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
-
- try {
- if ($this->isFinder && !isset($this->loaded[$class])) {
- $this->loaded[$class] = true;
- if (!$file = $this->classLoader[0]->findFile($class) ?: false) {
- // no-op
- } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) {
- include $file;
-
- return;
- } elseif (false === include $file) {
- return;
- }
- } else {
- ($this->classLoader)($class);
- $file = false;
- }
- } finally {
- error_reporting($e);
- }
-
- $this->checkClass($class, $file);
- }
-
- private function checkClass(string $class, string $file = null)
- {
- $exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
-
- if (null !== $file && $class && '\\' === $class[0]) {
- $class = substr($class, 1);
- }
-
- if ($exists) {
- if (isset(self::$checkedClasses[$class])) {
- return;
- }
- self::$checkedClasses[$class] = true;
-
- $refl = new \ReflectionClass($class);
- if (null === $file && $refl->isInternal()) {
- return;
- }
- $name = $refl->getName();
-
- if ($name !== $class && 0 === strcasecmp($name, $class)) {
- throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
- }
-
- $deprecations = $this->checkAnnotations($refl, $name);
-
- foreach ($deprecations as $message) {
- @trigger_error($message, E_USER_DEPRECATED);
- }
- }
-
- if (!$file) {
- return;
- }
-
- if (!$exists) {
- if (false !== strpos($class, '/')) {
- throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class));
- }
-
- throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
- }
-
- if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) {
- throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2]));
- }
- }
-
- public function checkAnnotations(\ReflectionClass $refl, $class)
- {
- $deprecations = [];
-
- // Don't trigger deprecations for classes in the same vendor
- if (2 > $len = 1 + (strpos($class, '\\') ?: strpos($class, '_'))) {
- $len = 0;
- $ns = '';
- } else {
- $ns = str_replace('_', '\\', substr($class, 0, $len));
- }
-
- // Detect annotations on the class
- if (false !== $doc = $refl->getDocComment()) {
- foreach (['final', 'deprecated', 'internal'] as $annotation) {
- if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
- self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : '';
- }
- }
-
- if ($refl->isInterface() && false !== strpos($doc, 'method') && preg_match_all('#\n \* @method\s+(static\s+)?+(?:[\w\|&\[\]\\\]+\s+)?(\w+(?:\s*\([^\)]*\))?)+(.+?([[:punct:]]\s*)?)?(?=\r?\n \*(?: @|/$|\r?\n))#', $doc, $notice, PREG_SET_ORDER)) {
- foreach ($notice as $method) {
- $static = '' !== $method[1];
- $name = $method[2];
- $description = $method[3] ?? null;
- if (false === strpos($name, '(')) {
- $name .= '()';
- }
- if (null !== $description) {
- $description = trim($description);
- if (!isset($method[4])) {
- $description .= '.';
- }
- }
- self::$method[$class][] = [$class, $name, $static, $description];
- }
- }
- }
-
- $parent = get_parent_class($class);
- $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent ?: null);
- if ($parent) {
- $parentAndOwnInterfaces[$parent] = $parent;
-
- if (!isset(self::$checkedClasses[$parent])) {
- $this->checkClass($parent);
- }
-
- if (isset(self::$final[$parent])) {
- $deprecations[] = sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $class);
- }
- }
-
- // Detect if the parent is annotated
- foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) {
- if (!isset(self::$checkedClasses[$use])) {
- $this->checkClass($use);
- }
- if (isset(self::$deprecated[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len) && !isset(self::$deprecated[$class])) {
- $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
- $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
-
- $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]);
- }
- if (isset(self::$internal[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len)) {
- $deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $class);
- }
- if (isset(self::$method[$use])) {
- if ($refl->isAbstract()) {
- if (isset(self::$method[$class])) {
- self::$method[$class] = array_merge(self::$method[$class], self::$method[$use]);
- } else {
- self::$method[$class] = self::$method[$use];
- }
- } elseif (!$refl->isInterface()) {
- $hasCall = $refl->hasMethod('__call');
- $hasStaticCall = $refl->hasMethod('__callStatic');
- foreach (self::$method[$use] as $method) {
- list($interface, $name, $static, $description) = $method;
- if ($static ? $hasStaticCall : $hasCall) {
- continue;
- }
- $realName = substr($name, 0, strpos($name, '('));
- if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) {
- $deprecations[] = sprintf('Class "%s" should implement method "%s::%s"%s', $class, ($static ? 'static ' : '').$interface, $name, null == $description ? '.' : ': '.$description);
- }
- }
- }
- }
- }
-
- if (trait_exists($class)) {
- return $deprecations;
- }
-
- // Inherit @final, @internal and @param annotations for methods
- self::$finalMethods[$class] = [];
- self::$internalMethods[$class] = [];
- self::$annotatedParameters[$class] = [];
- foreach ($parentAndOwnInterfaces as $use) {
- foreach (['finalMethods', 'internalMethods', 'annotatedParameters'] as $property) {
- if (isset(self::${$property}[$use])) {
- self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use];
- }
- }
- }
-
- foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
- if ($method->class !== $class) {
- continue;
- }
-
- if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
- list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
- $deprecations[] = sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class);
- }
-
- if (isset(self::$internalMethods[$class][$method->name])) {
- list($declaringClass, $message) = self::$internalMethods[$class][$method->name];
- if (strncmp($ns, $declaringClass, $len)) {
- $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class);
- }
- }
-
- // To read method annotations
- $doc = $method->getDocComment();
-
- if (isset(self::$annotatedParameters[$class][$method->name])) {
- $definedParameters = [];
- foreach ($method->getParameters() as $parameter) {
- $definedParameters[$parameter->name] = true;
- }
-
- foreach (self::$annotatedParameters[$class][$method->name] as $parameterName => $deprecation) {
- if (!isset($definedParameters[$parameterName]) && !($doc && preg_match("/\\n\\s+\\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\\\${$parameterName}\\b/", $doc))) {
- $deprecations[] = sprintf($deprecation, $class);
- }
- }
- }
-
- if (!$doc) {
- continue;
- }
-
- $finalOrInternal = false;
-
- foreach (['final', 'internal'] as $annotation) {
- if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
- $message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : '';
- self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message];
- $finalOrInternal = true;
- }
- }
-
- if ($finalOrInternal || $method->isConstructor() || false === strpos($doc, '@param') || StatelessInvocation::class === $class) {
- continue;
- }
- if (!preg_match_all('#\n\s+\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\$([a-zA-Z0-9_\x7f-\xff]++)#', $doc, $matches, PREG_SET_ORDER)) {
- continue;
- }
- if (!isset(self::$annotatedParameters[$class][$method->name])) {
- $definedParameters = [];
- foreach ($method->getParameters() as $parameter) {
- $definedParameters[$parameter->name] = true;
- }
- }
- foreach ($matches as list(, $parameterType, $parameterName)) {
- if (!isset($definedParameters[$parameterName])) {
- $parameterType = trim($parameterType);
- self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($class) ? 'interface' : 'parent class', $method->class);
- }
- }
- }
-
- return $deprecations;
- }
-
- /**
- * @param string $file
- * @param string $class
- *
- * @return array|null
- */
- public function checkCase(\ReflectionClass $refl, $file, $class)
- {
- $real = explode('\\', $class.strrchr($file, '.'));
- $tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file));
-
- $i = \count($tail) - 1;
- $j = \count($real) - 1;
-
- while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
- --$i;
- --$j;
- }
-
- array_splice($tail, 0, $i + 1);
-
- if (!$tail) {
- return null;
- }
-
- $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
- $tailLen = \strlen($tail);
- $real = $refl->getFileName();
-
- if (2 === self::$caseCheck) {
- $real = $this->darwinRealpath($real);
- }
-
- if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
- && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
- ) {
- return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)];
- }
-
- return null;
- }
-
- /**
- * `realpath` on MacOSX doesn't normalize the case of characters.
- */
- private function darwinRealpath(string $real): string
- {
- $i = 1 + strrpos($real, '/');
- $file = substr($real, $i);
- $real = substr($real, 0, $i);
-
- if (isset(self::$darwinCache[$real])) {
- $kDir = $real;
- } else {
- $kDir = strtolower($real);
-
- if (isset(self::$darwinCache[$kDir])) {
- $real = self::$darwinCache[$kDir][0];
- } else {
- $dir = getcwd();
-
- if (!@chdir($real)) {
- return $real.$file;
- }
-
- $real = getcwd().'/';
- chdir($dir);
-
- $dir = $real;
- $k = $kDir;
- $i = \strlen($dir) - 1;
- while (!isset(self::$darwinCache[$k])) {
- self::$darwinCache[$k] = [$dir, []];
- self::$darwinCache[$dir] = &self::$darwinCache[$k];
-
- while ('/' !== $dir[--$i]) {
- }
- $k = substr($k, 0, ++$i);
- $dir = substr($dir, 0, $i--);
- }
- }
- }
-
- $dirFiles = self::$darwinCache[$kDir][1];
-
- if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) {
- // Get the file name from "file_name.php(123) : eval()'d code"
- $file = substr($file, 0, strrpos($file, '(', -17));
- }
-
- if (isset($dirFiles[$file])) {
- return $real.$dirFiles[$file];
- }
-
- $kFile = strtolower($file);
-
- if (!isset($dirFiles[$kFile])) {
- foreach (scandir($real, 2) as $f) {
- if ('.' !== $f[0]) {
- $dirFiles[$f] = $f;
- if ($f === $file) {
- $kFile = $k = $file;
- } elseif ($f !== $k = strtolower($f)) {
- $dirFiles[$k] = $f;
- }
- }
- }
- self::$darwinCache[$kDir][1] = $dirFiles;
- }
-
- return $real.$dirFiles[$kFile];
- }
-
- /**
- * `class_implements` includes interfaces from the parents so we have to manually exclude them.
- *
- * @return string[]
- */
- private function getOwnInterfaces(string $class, ?string $parent): array
- {
- $ownInterfaces = class_implements($class, false);
-
- if ($parent) {
- foreach (class_implements($parent, false) as $interface) {
- unset($ownInterfaces[$interface]);
- }
- }
-
- foreach ($ownInterfaces as $interface) {
- foreach (class_implements($interface) as $interface) {
- unset($ownInterfaces[$interface]);
- }
- }
-
- return $ownInterfaces;
- }
-}
diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php
deleted file mode 100644
index b7ca74dc00d18..0000000000000
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ /dev/null
@@ -1,719 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\LogLevel;
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Symfony\Component\Debug\Exception\FatalThrowableError;
-use Symfony\Component\Debug\Exception\FlattenException;
-use Symfony\Component\Debug\Exception\OutOfMemoryException;
-use Symfony\Component\Debug\Exception\SilencedErrorContext;
-use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
-use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
-use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
-use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ErrorHandler::class, \Symfony\Component\ErrorHandler\ErrorHandler::class), E_USER_DEPRECATED);
-
-/**
- * A generic ErrorHandler for the PHP engine.
- *
- * Provides five bit fields that control how errors are handled:
- * - thrownErrors: errors thrown as \ErrorException
- * - loggedErrors: logged errors, when not @-silenced
- * - scopedErrors: errors thrown or logged with their local context
- * - tracedErrors: errors logged with their stack trace
- * - screamedErrors: never @-silenced errors
- *
- * Each error level can be logged by a dedicated PSR-3 logger object.
- * Screaming only applies to logging.
- * Throwing takes precedence over logging.
- * Uncaught exceptions are logged as E_ERROR.
- * E_DEPRECATED and E_USER_DEPRECATED levels never throw.
- * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
- * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
- * As errors have a performance cost, repeated errors are all logged, so that the developer
- * can see them and weight them as more important to fix than others of the same level.
- *
- * @author Nicolas Grekas
- * @author Grégoire Pineau
- *
- * @final since Symfony 4.3
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorHandler instead.
- */
-class ErrorHandler
-{
- private $levels = [
- E_DEPRECATED => 'Deprecated',
- E_USER_DEPRECATED => 'User Deprecated',
- E_NOTICE => 'Notice',
- E_USER_NOTICE => 'User Notice',
- E_STRICT => 'Runtime Notice',
- E_WARNING => 'Warning',
- E_USER_WARNING => 'User Warning',
- E_COMPILE_WARNING => 'Compile Warning',
- E_CORE_WARNING => 'Core Warning',
- E_USER_ERROR => 'User Error',
- E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
- E_COMPILE_ERROR => 'Compile Error',
- E_PARSE => 'Parse Error',
- E_ERROR => 'Error',
- E_CORE_ERROR => 'Core Error',
- ];
-
- private $loggers = [
- E_DEPRECATED => [null, LogLevel::INFO],
- E_USER_DEPRECATED => [null, LogLevel::INFO],
- E_NOTICE => [null, LogLevel::WARNING],
- E_USER_NOTICE => [null, LogLevel::WARNING],
- E_STRICT => [null, LogLevel::WARNING],
- E_WARNING => [null, LogLevel::WARNING],
- E_USER_WARNING => [null, LogLevel::WARNING],
- E_COMPILE_WARNING => [null, LogLevel::WARNING],
- E_CORE_WARNING => [null, LogLevel::WARNING],
- E_USER_ERROR => [null, LogLevel::CRITICAL],
- E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
- E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
- E_PARSE => [null, LogLevel::CRITICAL],
- E_ERROR => [null, LogLevel::CRITICAL],
- E_CORE_ERROR => [null, LogLevel::CRITICAL],
- ];
-
- private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
- private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
- private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
- private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
- private $loggedErrors = 0;
- private $traceReflector;
-
- private $isRecursive = 0;
- private $isRoot = false;
- private $exceptionHandler;
- private $bootstrappingLogger;
-
- private static $reservedMemory;
- private static $toStringException = null;
- private static $silencedErrorCache = [];
- private static $silencedErrorCount = 0;
- private static $exitCode = 0;
-
- /**
- * Registers the error handler.
- *
- * @param self|null $handler The handler to register
- * @param bool $replace Whether to replace or not any existing handler
- *
- * @return self The registered error handler
- */
- public static function register(self $handler = null, $replace = true)
- {
- if (null === self::$reservedMemory) {
- self::$reservedMemory = str_repeat('x', 10240);
- register_shutdown_function(__CLASS__.'::handleFatalError');
- }
-
- if ($handlerIsNew = null === $handler) {
- $handler = new static();
- }
-
- if (null === $prev = set_error_handler([$handler, 'handleError'])) {
- restore_error_handler();
- // Specifying the error types earlier would expose us to https://bugs.php.net/63206
- set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors);
- $handler->isRoot = true;
- }
-
- if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) {
- $handler = $prev[0];
- $replace = false;
- }
- if (!$replace && $prev) {
- restore_error_handler();
- $handlerIsRegistered = \is_array($prev) && $handler === $prev[0];
- } else {
- $handlerIsRegistered = true;
- }
- if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) {
- restore_exception_handler();
- if (!$handlerIsRegistered) {
- $handler = $prev[0];
- } elseif ($handler !== $prev[0] && $replace) {
- set_exception_handler([$handler, 'handleException']);
- $p = $prev[0]->setExceptionHandler(null);
- $handler->setExceptionHandler($p);
- $prev[0]->setExceptionHandler($p);
- }
- } else {
- $handler->setExceptionHandler($prev);
- }
-
- $handler->throwAt(E_ALL & $handler->thrownErrors, true);
-
- return $handler;
- }
-
- public function __construct(BufferingLogger $bootstrappingLogger = null)
- {
- if ($bootstrappingLogger) {
- $this->bootstrappingLogger = $bootstrappingLogger;
- $this->setDefaultLogger($bootstrappingLogger);
- }
- $this->traceReflector = new \ReflectionProperty('Exception', 'trace');
- $this->traceReflector->setAccessible(true);
- }
-
- /**
- * Sets a logger to non assigned errors levels.
- *
- * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
- * @param bool $replace Whether to replace or not any existing logger
- */
- public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
- {
- $loggers = [];
-
- if (\is_array($levels)) {
- foreach ($levels as $type => $logLevel) {
- if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
- $loggers[$type] = [$logger, $logLevel];
- }
- }
- } else {
- if (null === $levels) {
- $levels = E_ALL;
- }
- foreach ($this->loggers as $type => $log) {
- if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
- $log[0] = $logger;
- $loggers[$type] = $log;
- }
- }
- }
-
- $this->setLoggers($loggers);
- }
-
- /**
- * Sets a logger for each error level.
- *
- * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
- *
- * @return array The previous map
- *
- * @throws \InvalidArgumentException
- */
- public function setLoggers(array $loggers)
- {
- $prevLogged = $this->loggedErrors;
- $prev = $this->loggers;
- $flush = [];
-
- foreach ($loggers as $type => $log) {
- if (!isset($prev[$type])) {
- throw new \InvalidArgumentException('Unknown error type: '.$type);
- }
- if (!\is_array($log)) {
- $log = [$log];
- } elseif (!\array_key_exists(0, $log)) {
- throw new \InvalidArgumentException('No logger provided');
- }
- if (null === $log[0]) {
- $this->loggedErrors &= ~$type;
- } elseif ($log[0] instanceof LoggerInterface) {
- $this->loggedErrors |= $type;
- } else {
- throw new \InvalidArgumentException('Invalid logger provided');
- }
- $this->loggers[$type] = $log + $prev[$type];
-
- if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
- $flush[$type] = $type;
- }
- }
- $this->reRegister($prevLogged | $this->thrownErrors);
-
- if ($flush) {
- foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
- $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
- if (!isset($flush[$type])) {
- $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
- } elseif ($this->loggers[$type][0]) {
- $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
- }
- }
- }
-
- return $prev;
- }
-
- /**
- * Sets a user exception handler.
- *
- * @param callable $handler A handler that will be called on Exception
- *
- * @return callable|null The previous exception handler
- */
- public function setExceptionHandler(callable $handler = null)
- {
- $prev = $this->exceptionHandler;
- $this->exceptionHandler = $handler;
-
- return $prev;
- }
-
- /**
- * Sets the PHP error levels that throw an exception when a PHP error occurs.
- *
- * @param int $levels A bit field of E_* constants for thrown errors
- * @param bool $replace Replace or amend the previous value
- *
- * @return int The previous value
- */
- public function throwAt($levels, $replace = false)
- {
- $prev = $this->thrownErrors;
- $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
- if (!$replace) {
- $this->thrownErrors |= $prev;
- }
- $this->reRegister($prev | $this->loggedErrors);
-
- return $prev;
- }
-
- /**
- * Sets the PHP error levels for which local variables are preserved.
- *
- * @param int $levels A bit field of E_* constants for scoped errors
- * @param bool $replace Replace or amend the previous value
- *
- * @return int The previous value
- */
- public function scopeAt($levels, $replace = false)
- {
- $prev = $this->scopedErrors;
- $this->scopedErrors = (int) $levels;
- if (!$replace) {
- $this->scopedErrors |= $prev;
- }
-
- return $prev;
- }
-
- /**
- * Sets the PHP error levels for which the stack trace is preserved.
- *
- * @param int $levels A bit field of E_* constants for traced errors
- * @param bool $replace Replace or amend the previous value
- *
- * @return int The previous value
- */
- public function traceAt($levels, $replace = false)
- {
- $prev = $this->tracedErrors;
- $this->tracedErrors = (int) $levels;
- if (!$replace) {
- $this->tracedErrors |= $prev;
- }
-
- return $prev;
- }
-
- /**
- * Sets the error levels where the @-operator is ignored.
- *
- * @param int $levels A bit field of E_* constants for screamed errors
- * @param bool $replace Replace or amend the previous value
- *
- * @return int The previous value
- */
- public function screamAt($levels, $replace = false)
- {
- $prev = $this->screamedErrors;
- $this->screamedErrors = (int) $levels;
- if (!$replace) {
- $this->screamedErrors |= $prev;
- }
-
- return $prev;
- }
-
- /**
- * Re-registers as a PHP error handler if levels changed.
- */
- private function reRegister(int $prev)
- {
- if ($prev !== $this->thrownErrors | $this->loggedErrors) {
- $handler = set_error_handler('var_dump');
- $handler = \is_array($handler) ? $handler[0] : null;
- restore_error_handler();
- if ($handler === $this) {
- restore_error_handler();
- if ($this->isRoot) {
- set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors);
- } else {
- set_error_handler([$this, 'handleError']);
- }
- }
- }
- }
-
- /**
- * Handles errors by filtering then logging them according to the configured bit fields.
- *
- * @param int $type One of the E_* constants
- * @param string $message
- * @param string $file
- * @param int $line
- *
- * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
- *
- * @throws \ErrorException When $this->thrownErrors requests so
- *
- * @internal
- */
- public function handleError($type, $message, $file, $line)
- {
- if (\PHP_VERSION_ID >= 70300 && E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) {
- $type = E_DEPRECATED;
- }
-
- // Level is the current error reporting level to manage silent error.
- $level = error_reporting();
- $silenced = 0 === ($level & $type);
- // Strong errors are not authorized to be silenced.
- $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
- $log = $this->loggedErrors & $type;
- $throw = $this->thrownErrors & $type & $level;
- $type &= $level | $this->screamedErrors;
-
- if (!$type || (!$log && !$throw)) {
- return !$silenced && $type && $log;
- }
- $scope = $this->scopedErrors & $type;
-
- if (4 < $numArgs = \func_num_args()) {
- $context = $scope ? (func_get_arg(4) ?: []) : [];
- } else {
- $context = [];
- }
-
- if (isset($context['GLOBALS']) && $scope) {
- $e = $context; // Whatever the signature of the method,
- unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
- $context = $e;
- }
-
- if (false !== strpos($message, "class@anonymous\0")) {
- $logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage();
- } else {
- $logMessage = $this->levels[$type].': '.$message;
- }
-
- if (null !== self::$toStringException) {
- $errorAsException = self::$toStringException;
- self::$toStringException = null;
- } elseif (!$throw && !($type & $level)) {
- if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
- $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : [];
- $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace);
- } elseif (isset(self::$silencedErrorCache[$id][$message])) {
- $lightTrace = null;
- $errorAsException = self::$silencedErrorCache[$id][$message];
- ++$errorAsException->count;
- } else {
- $lightTrace = [];
- $errorAsException = null;
- }
-
- if (100 < ++self::$silencedErrorCount) {
- self::$silencedErrorCache = $lightTrace = [];
- self::$silencedErrorCount = 1;
- }
- if ($errorAsException) {
- self::$silencedErrorCache[$id][$message] = $errorAsException;
- }
- if (null === $lightTrace) {
- return true;
- }
- } else {
- $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
-
- if ($throw || $this->tracedErrors & $type) {
- $backtrace = $errorAsException->getTrace();
- $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
- $this->traceReflector->setValue($errorAsException, $lightTrace);
- } else {
- $this->traceReflector->setValue($errorAsException, []);
- $backtrace = [];
- }
- }
-
- if ($throw) {
- if (\PHP_VERSION_ID < 70400 && E_USER_ERROR & $type) {
- for ($i = 1; isset($backtrace[$i]); ++$i) {
- if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
- && '__toString' === $backtrace[$i]['function']
- && '->' === $backtrace[$i]['type']
- && !isset($backtrace[$i - 1]['class'])
- && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
- ) {
- // Here, we know trigger_error() has been called from __toString().
- // PHP triggers a fatal error when throwing from __toString().
- // A small convention allows working around the limitation:
- // given a caught $e exception in __toString(), quitting the method with
- // `return trigger_error($e, E_USER_ERROR);` allows this error handler
- // to make $e get through the __toString() barrier.
-
- foreach ($context as $e) {
- if ($e instanceof \Throwable && $e->__toString() === $message) {
- self::$toStringException = $e;
-
- return true;
- }
- }
-
- // Display the original error message instead of the default one.
- $this->handleException($errorAsException);
-
- // Stop the process by giving back the error to the native handler.
- return false;
- }
- }
- }
-
- throw $errorAsException;
- }
-
- if ($this->isRecursive) {
- $log = 0;
- } else {
- if (!\defined('HHVM_VERSION')) {
- $currentErrorHandler = set_error_handler('var_dump');
- restore_error_handler();
- }
-
- try {
- $this->isRecursive = true;
- $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
- $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []);
- } finally {
- $this->isRecursive = false;
-
- if (!\defined('HHVM_VERSION')) {
- set_error_handler($currentErrorHandler);
- }
- }
- }
-
- return !$silenced && $type && $log;
- }
-
- /**
- * Handles an exception by logging then forwarding it to another handler.
- *
- * @param \Exception|\Throwable $exception An exception to handle
- * @param array $error An array as returned by error_get_last()
- *
- * @internal
- */
- public function handleException($exception, array $error = null)
- {
- if (null === $error) {
- self::$exitCode = 255;
- }
- if (!$exception instanceof \Exception) {
- $exception = new FatalThrowableError($exception);
- }
- $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
- $handlerException = null;
-
- if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
- if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
- $message = (new FlattenException())->setMessage($message)->getMessage();
- }
- if ($exception instanceof FatalErrorException) {
- if ($exception instanceof FatalThrowableError) {
- $error = [
- 'type' => $type,
- 'message' => $message,
- 'file' => $exception->getFile(),
- 'line' => $exception->getLine(),
- ];
- } else {
- $message = 'Fatal '.$message;
- }
- } elseif ($exception instanceof \ErrorException) {
- $message = 'Uncaught '.$message;
- } else {
- $message = 'Uncaught Exception: '.$message;
- }
- }
- if ($this->loggedErrors & $type) {
- try {
- $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]);
- } catch (\Throwable $handlerException) {
- }
- }
- if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
- foreach ($this->getFatalErrorHandlers() as $handler) {
- if ($e = $handler->handleError($error, $exception)) {
- $exception = $e;
- break;
- }
- }
- }
- $exceptionHandler = $this->exceptionHandler;
- $this->exceptionHandler = null;
- try {
- if (null !== $exceptionHandler) {
- $exceptionHandler($exception);
-
- return;
- }
- $handlerException = $handlerException ?: $exception;
- } catch (\Throwable $handlerException) {
- }
- if ($exception === $handlerException) {
- self::$reservedMemory = null; // Disable the fatal error handler
- throw $exception; // Give back $exception to the native handler
- }
- $this->handleException($handlerException);
- }
-
- /**
- * Shutdown registered function for handling PHP fatal errors.
- *
- * @param array $error An array as returned by error_get_last()
- *
- * @internal
- */
- public static function handleFatalError(array $error = null)
- {
- if (null === self::$reservedMemory) {
- return;
- }
-
- $handler = self::$reservedMemory = null;
- $handlers = [];
- $previousHandler = null;
- $sameHandlerLimit = 10;
-
- while (!\is_array($handler) || !$handler[0] instanceof self) {
- $handler = set_exception_handler('var_dump');
- restore_exception_handler();
-
- if (!$handler) {
- break;
- }
- restore_exception_handler();
-
- if ($handler !== $previousHandler) {
- array_unshift($handlers, $handler);
- $previousHandler = $handler;
- } elseif (0 === --$sameHandlerLimit) {
- $handler = null;
- break;
- }
- }
- foreach ($handlers as $h) {
- set_exception_handler($h);
- }
- if (!$handler) {
- return;
- }
- if ($handler !== $h) {
- $handler[0]->setExceptionHandler($h);
- }
- $handler = $handler[0];
- $handlers = [];
-
- if ($exit = null === $error) {
- $error = error_get_last();
- }
-
- if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
- // Let's not throw anymore but keep logging
- $handler->throwAt(0, true);
- $trace = isset($error['backtrace']) ? $error['backtrace'] : null;
-
- if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
- $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
- } else {
- $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
- }
- } else {
- $exception = null;
- }
-
- try {
- if (null !== $exception) {
- self::$exitCode = 255;
- $handler->handleException($exception, $error);
- }
- } catch (FatalErrorException $e) {
- // Ignore this re-throw
- }
-
- if ($exit && self::$exitCode) {
- $exitCode = self::$exitCode;
- register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
- }
- }
-
- /**
- * Gets the fatal error handlers.
- *
- * Override this method if you want to define more fatal error handlers.
- *
- * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
- */
- protected function getFatalErrorHandlers()
- {
- return [
- new UndefinedFunctionFatalErrorHandler(),
- new UndefinedMethodFatalErrorHandler(),
- new ClassNotFoundFatalErrorHandler(),
- ];
- }
-
- /**
- * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader.
- */
- private function cleanTrace(array $backtrace, int $type, string $file, int $line, bool $throw): array
- {
- $lightTrace = $backtrace;
-
- for ($i = 0; isset($backtrace[$i]); ++$i) {
- if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
- $lightTrace = \array_slice($lightTrace, 1 + $i);
- break;
- }
- }
- if (class_exists(DebugClassLoader::class, false)) {
- for ($i = \count($lightTrace) - 2; 0 < $i; --$i) {
- if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) {
- array_splice($lightTrace, --$i, 2);
- }
- }
- }
- if (!($throw || $this->scopedErrors & $type)) {
- for ($i = 0; isset($lightTrace[$i]); ++$i) {
- unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);
- }
- }
-
- return $lightTrace;
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php b/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php
deleted file mode 100644
index 6c87f98c55be8..0000000000000
--- a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ClassNotFoundException::class, \Symfony\Component\ErrorHandler\Error\ClassNotFoundError::class), E_USER_DEPRECATED);
-
-/**
- * Class (or Trait or Interface) Not Found Exception.
- *
- * @author Konstanton Myakshin
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\ClassNotFoundError instead.
- */
-class ClassNotFoundException extends FatalErrorException
-{
- public function __construct(string $message, \ErrorException $previous)
- {
- parent::__construct(
- $message,
- $previous->getCode(),
- $previous->getSeverity(),
- $previous->getFile(),
- $previous->getLine(),
- null,
- true,
- null,
- $previous->getPrevious()
- );
- $this->setTrace($previous->getTrace());
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/FatalErrorException.php b/src/Symfony/Component/Debug/Exception/FatalErrorException.php
deleted file mode 100644
index 4eb445dcdbd5a..0000000000000
--- a/src/Symfony/Component/Debug/Exception/FatalErrorException.php
+++ /dev/null
@@ -1,81 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalErrorException::class, \Symfony\Component\ErrorHandler\Error\FatalError::class), E_USER_DEPRECATED);
-
-/**
- * Fatal Error Exception.
- *
- * @author Konstanton Myakshin
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\FatalError instead.
- */
-class FatalErrorException extends \ErrorException
-{
- public function __construct(string $message, int $code, int $severity, string $filename, int $lineno, int $traceOffset = null, bool $traceArgs = true, array $trace = null, \Throwable $previous = null)
- {
- parent::__construct($message, $code, $severity, $filename, $lineno, $previous);
-
- if (null !== $trace) {
- if (!$traceArgs) {
- foreach ($trace as &$frame) {
- unset($frame['args'], $frame['this'], $frame);
- }
- }
-
- $this->setTrace($trace);
- } elseif (null !== $traceOffset) {
- if (\function_exists('xdebug_get_function_stack')) {
- $trace = xdebug_get_function_stack();
- if (0 < $traceOffset) {
- array_splice($trace, -$traceOffset);
- }
-
- foreach ($trace as &$frame) {
- if (!isset($frame['type'])) {
- // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
- if (isset($frame['class'])) {
- $frame['type'] = '::';
- }
- } elseif ('dynamic' === $frame['type']) {
- $frame['type'] = '->';
- } elseif ('static' === $frame['type']) {
- $frame['type'] = '::';
- }
-
- // XDebug also has a different name for the parameters array
- if (!$traceArgs) {
- unset($frame['params'], $frame['args']);
- } elseif (isset($frame['params']) && !isset($frame['args'])) {
- $frame['args'] = $frame['params'];
- unset($frame['params']);
- }
- }
-
- unset($frame);
- $trace = array_reverse($trace);
- } else {
- $trace = [];
- }
-
- $this->setTrace($trace);
- }
- }
-
- protected function setTrace($trace)
- {
- $traceReflector = new \ReflectionProperty('Exception', 'trace');
- $traceReflector->setAccessible(true);
- $traceReflector->setValue($this, $trace);
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php b/src/Symfony/Component/Debug/Exception/FatalThrowableError.php
deleted file mode 100644
index e13b0172f0588..0000000000000
--- a/src/Symfony/Component/Debug/Exception/FatalThrowableError.php
+++ /dev/null
@@ -1,55 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4.', FatalThrowableError::class), E_USER_DEPRECATED);
-
-/**
- * Fatal Throwable Error.
- *
- * @author Nicolas Grekas
- *
- * @deprecated since Symfony 4.4
- */
-class FatalThrowableError extends FatalErrorException
-{
- private $originalClassName;
-
- public function __construct(\Throwable $e)
- {
- $this->originalClassName = \get_class($e);
-
- if ($e instanceof \ParseError) {
- $severity = E_PARSE;
- } elseif ($e instanceof \TypeError) {
- $severity = E_RECOVERABLE_ERROR;
- } else {
- $severity = E_ERROR;
- }
-
- \ErrorException::__construct(
- $e->getMessage(),
- $e->getCode(),
- $severity,
- $e->getFile(),
- $e->getLine(),
- $e->getPrevious()
- );
-
- $this->setTrace($e->getTrace());
- }
-
- public function getOriginalClassName(): string
- {
- return $this->originalClassName;
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/FlattenException.php b/src/Symfony/Component/Debug/Exception/FlattenException.php
deleted file mode 100644
index a4cb517cb20fb..0000000000000
--- a/src/Symfony/Component/Debug/Exception/FlattenException.php
+++ /dev/null
@@ -1,367 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
-use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
-
-/**
- * FlattenException wraps a PHP Error or Exception to be able to serialize it.
- *
- * Basically, this class removes all objects from the trace.
- *
- * @author Fabien Potencier
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\FlattenException instead.
- */
-class FlattenException
-{
- private $message;
- private $code;
- private $previous;
- private $trace;
- private $traceAsString;
- private $class;
- private $statusCode;
- private $headers;
- private $file;
- private $line;
-
- /**
- * @return static
- */
- public static function create(\Exception $exception, $statusCode = null, array $headers = [])
- {
- return static::createFromThrowable($exception, $statusCode, $headers);
- }
-
- /**
- * @return static
- */
- public static function createFromThrowable(\Throwable $exception, int $statusCode = null, array $headers = [])
- {
- $e = new static();
- $e->setMessage($exception->getMessage());
- $e->setCode($exception->getCode());
-
- if ($exception instanceof HttpExceptionInterface) {
- $statusCode = $exception->getStatusCode();
- $headers = array_merge($headers, $exception->getHeaders());
- } elseif ($exception instanceof RequestExceptionInterface) {
- $statusCode = 400;
- }
-
- if (null === $statusCode) {
- $statusCode = 500;
- }
-
- $e->setStatusCode($statusCode);
- $e->setHeaders($headers);
- $e->setTraceFromThrowable($exception);
- $e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception));
- $e->setFile($exception->getFile());
- $e->setLine($exception->getLine());
-
- $previous = $exception->getPrevious();
-
- if ($previous instanceof \Throwable) {
- $e->setPrevious(static::createFromThrowable($previous));
- }
-
- return $e;
- }
-
- public function toArray()
- {
- $exceptions = [];
- foreach (array_merge([$this], $this->getAllPrevious()) as $exception) {
- $exceptions[] = [
- 'message' => $exception->getMessage(),
- 'class' => $exception->getClass(),
- 'trace' => $exception->getTrace(),
- ];
- }
-
- return $exceptions;
- }
-
- public function getStatusCode()
- {
- return $this->statusCode;
- }
-
- /**
- * @return $this
- */
- public function setStatusCode($code)
- {
- $this->statusCode = $code;
-
- return $this;
- }
-
- public function getHeaders()
- {
- return $this->headers;
- }
-
- /**
- * @return $this
- */
- public function setHeaders(array $headers)
- {
- $this->headers = $headers;
-
- return $this;
- }
-
- public function getClass()
- {
- return $this->class;
- }
-
- /**
- * @return $this
- */
- public function setClass($class)
- {
- $this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
-
- return $this;
- }
-
- public function getFile()
- {
- return $this->file;
- }
-
- /**
- * @return $this
- */
- public function setFile($file)
- {
- $this->file = $file;
-
- return $this;
- }
-
- public function getLine()
- {
- return $this->line;
- }
-
- /**
- * @return $this
- */
- public function setLine($line)
- {
- $this->line = $line;
-
- return $this;
- }
-
- public function getMessage()
- {
- return $this->message;
- }
-
- /**
- * @return $this
- */
- public function setMessage($message)
- {
- if (false !== strpos($message, "class@anonymous\0")) {
- $message = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
- return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
- }, $message);
- }
-
- $this->message = $message;
-
- return $this;
- }
-
- public function getCode()
- {
- return $this->code;
- }
-
- /**
- * @return $this
- */
- public function setCode($code)
- {
- $this->code = $code;
-
- return $this;
- }
-
- public function getPrevious()
- {
- return $this->previous;
- }
-
- /**
- * @return $this
- */
- public function setPrevious(self $previous)
- {
- $this->previous = $previous;
-
- return $this;
- }
-
- public function getAllPrevious()
- {
- $exceptions = [];
- $e = $this;
- while ($e = $e->getPrevious()) {
- $exceptions[] = $e;
- }
-
- return $exceptions;
- }
-
- public function getTrace()
- {
- return $this->trace;
- }
-
- /**
- * @deprecated since 4.1, use {@see setTraceFromThrowable()} instead.
- */
- public function setTraceFromException(\Exception $exception)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED);
-
- $this->setTraceFromThrowable($exception);
- }
-
- public function setTraceFromThrowable(\Throwable $throwable)
- {
- $this->traceAsString = $throwable->getTraceAsString();
-
- return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine());
- }
-
- /**
- * @return $this
- */
- public function setTrace($trace, $file, $line)
- {
- $this->trace = [];
- $this->trace[] = [
- 'namespace' => '',
- 'short_class' => '',
- 'class' => '',
- 'type' => '',
- 'function' => '',
- 'file' => $file,
- 'line' => $line,
- 'args' => [],
- ];
- foreach ($trace as $entry) {
- $class = '';
- $namespace = '';
- if (isset($entry['class'])) {
- $parts = explode('\\', $entry['class']);
- $class = array_pop($parts);
- $namespace = implode('\\', $parts);
- }
-
- $this->trace[] = [
- 'namespace' => $namespace,
- 'short_class' => $class,
- 'class' => isset($entry['class']) ? $entry['class'] : '',
- 'type' => isset($entry['type']) ? $entry['type'] : '',
- 'function' => isset($entry['function']) ? $entry['function'] : null,
- 'file' => isset($entry['file']) ? $entry['file'] : null,
- 'line' => isset($entry['line']) ? $entry['line'] : null,
- 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [],
- ];
- }
-
- return $this;
- }
-
- private function flattenArgs(array $args, int $level = 0, int &$count = 0): array
- {
- $result = [];
- foreach ($args as $key => $value) {
- if (++$count > 1e4) {
- return ['array', '*SKIPPED over 10000 entries*'];
- }
- if ($value instanceof \__PHP_Incomplete_Class) {
- // is_object() returns false on PHP<=7.1
- $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)];
- } elseif (\is_object($value)) {
- $result[$key] = ['object', \get_class($value)];
- } elseif (\is_array($value)) {
- if ($level > 10) {
- $result[$key] = ['array', '*DEEP NESTED ARRAY*'];
- } else {
- $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)];
- }
- } elseif (null === $value) {
- $result[$key] = ['null', null];
- } elseif (\is_bool($value)) {
- $result[$key] = ['boolean', $value];
- } elseif (\is_int($value)) {
- $result[$key] = ['integer', $value];
- } elseif (\is_float($value)) {
- $result[$key] = ['float', $value];
- } elseif (\is_resource($value)) {
- $result[$key] = ['resource', get_resource_type($value)];
- } else {
- $result[$key] = ['string', (string) $value];
- }
- }
-
- return $result;
- }
-
- private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value): string
- {
- $array = new \ArrayObject($value);
-
- return $array['__PHP_Incomplete_Class_Name'];
- }
-
- public function getTraceAsString()
- {
- return $this->traceAsString;
- }
-
- public function getAsString()
- {
- $message = '';
- $next = false;
-
- foreach (array_reverse(array_merge([$this], $this->getAllPrevious())) as $exception) {
- if ($next) {
- $message .= 'Next ';
- } else {
- $next = true;
- }
- $message .= $exception->getClass();
-
- if ('' != $exception->getMessage()) {
- $message .= ': '.$exception->getMessage();
- }
-
- $message .= ' in '.$exception->getFile().':'.$exception->getLine().
- "\nStack trace:\n".$exception->getTraceAsString()."\n\n";
- }
-
- return rtrim($message);
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php b/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php
deleted file mode 100644
index a4a90ee997962..0000000000000
--- a/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', OutOfMemoryException::class, \Symfony\Component\ErrorHandler\Error\OutOfMemoryError::class), E_USER_DEPRECATED);
-
-/**
- * Out of memory exception.
- *
- * @author Nicolas Grekas
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\OutOfMemoryError instead.
- */
-class OutOfMemoryException extends FatalErrorException
-{
-}
diff --git a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php b/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php
deleted file mode 100644
index e666d84c51ac3..0000000000000
--- a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php
+++ /dev/null
@@ -1,71 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', SilencedErrorContext::class, \Symfony\Component\ErrorHandler\Exception\SilencedErrorContext::class), E_USER_DEPRECATED);
-
-/**
- * Data Object that represents a Silenced Error.
- *
- * @author Grégoire Pineau
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext instead.
- */
-class SilencedErrorContext implements \JsonSerializable
-{
- public $count = 1;
-
- private $severity;
- private $file;
- private $line;
- private $trace;
-
- public function __construct(int $severity, string $file, int $line, array $trace = [], int $count = 1)
- {
- $this->severity = $severity;
- $this->file = $file;
- $this->line = $line;
- $this->trace = $trace;
- $this->count = $count;
- }
-
- public function getSeverity()
- {
- return $this->severity;
- }
-
- public function getFile()
- {
- return $this->file;
- }
-
- public function getLine()
- {
- return $this->line;
- }
-
- public function getTrace()
- {
- return $this->trace;
- }
-
- public function jsonSerialize()
- {
- return [
- 'severity' => $this->severity,
- 'file' => $this->file,
- 'line' => $this->line,
- 'trace' => $this->trace,
- 'count' => $this->count,
- ];
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php b/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php
deleted file mode 100644
index 0a7037aee8310..0000000000000
--- a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedFunctionException::class, \Symfony\Component\ErrorHandler\Error\UndefinedFunctionError::class), E_USER_DEPRECATED);
-
-/**
- * Undefined Function Exception.
- *
- * @author Konstanton Myakshin
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\UndefinedFunctionError instead.
- */
-class UndefinedFunctionException extends FatalErrorException
-{
- public function __construct(string $message, \ErrorException $previous)
- {
- parent::__construct(
- $message,
- $previous->getCode(),
- $previous->getSeverity(),
- $previous->getFile(),
- $previous->getLine(),
- null,
- true,
- null,
- $previous->getPrevious()
- );
- $this->setTrace($previous->getTrace());
- }
-}
diff --git a/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php b/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php
deleted file mode 100644
index 4d31c56d37e43..0000000000000
--- a/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Exception;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedMethodException::class, \Symfony\Component\ErrorHandler\Error\UndefinedMethodError::class), E_USER_DEPRECATED);
-
-/**
- * Undefined Method Exception.
- *
- * @author Grégoire Pineau
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\Error\UndefinedMethodError instead.
- */
-class UndefinedMethodException extends FatalErrorException
-{
- public function __construct(string $message, \ErrorException $previous)
- {
- parent::__construct(
- $message,
- $previous->getCode(),
- $previous->getSeverity(),
- $previous->getFile(),
- $previous->getLine(),
- null,
- true,
- null,
- $previous->getPrevious()
- );
- $this->setTrace($previous->getTrace());
- }
-}
diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php
deleted file mode 100644
index deee58b7f57fb..0000000000000
--- a/src/Symfony/Component/Debug/ExceptionHandler.php
+++ /dev/null
@@ -1,470 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-use Symfony\Component\Debug\Exception\FlattenException;
-use Symfony\Component\Debug\Exception\OutOfMemoryException;
-use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ExceptionHandler::class, \Symfony\Component\ErrorHandler\ErrorHandler::class), E_USER_DEPRECATED);
-
-/**
- * ExceptionHandler converts an exception to a Response object.
- *
- * It is mostly useful in debug mode to replace the default PHP/XDebug
- * output with something prettier and more useful.
- *
- * As this class is mainly used during Kernel boot, where nothing is yet
- * available, the Response content is always HTML.
- *
- * @author Fabien Potencier
- * @author Nicolas Grekas
- *
- * @final since Symfony 4.3
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorHandler instead.
- */
-class ExceptionHandler
-{
- private const GHOST_ADDONS = [
- '02-14' => self::GHOST_HEART,
- '02-29' => self::GHOST_PLUS,
- '10-18' => self::GHOST_GIFT,
- ];
-
- private const GHOST_GIFT = 'M124.005 5.36c.396-.715 1.119-1.648-.124-1.873-.346-.177-.692-.492-1.038-.141-.769.303-1.435.728-.627 1.523.36.514.685 1.634 1.092 1.758.242-.417.47-.842.697-1.266zm-1.699 1.977c-.706-1.26-1.274-2.612-2.138-3.774-1.051-1.123-3.122-.622-3.593.825-.625 1.431.724 3.14 2.251 2.96 1.159.02 2.324.072 3.48-.011zm5.867.043c1.502-.202 2.365-2.092 1.51-3.347-.757-1.34-2.937-1.387-3.698-.025-.659 1.1-1.23 2.25-1.835 3.38 1.336.077 2.686.06 4.023-.008zm2.487 1.611c.512-.45 2.494-.981.993-1.409-.372-.105-.805-.59-1.14-.457-.726.902-1.842 1.432-3.007 1.376-.228.075-1.391-.114-1.077.1.822.47 1.623.979 2.474 1.395.595-.317 1.173-.667 1.757-1.005zm-11.696.255l1.314-.765c-1.338-.066-2.87.127-3.881-.95-.285-.319-.559-.684-.954-.282-.473.326-1.929.66-.808 1.058.976.576 1.945 1.167 2.946 1.701.476-.223.926-.503 1.383-.762zm6.416 2.846c.567-.456 1.942-.89 1.987-1.38-1.282-.737-2.527-1.56-3.87-2.183-.461-.175-.835.094-1.207.328-1.1.654-2.225 1.267-3.288 1.978 1.39.86 2.798 1.695 4.219 2.504.725-.407 1.44-.83 2.16-1.247zm5.692 1.423l1.765-1.114c-.005-1.244.015-2.488-.019-3.732a77.306 77.306 0 0 0-3.51 2.084c-.126 1.282-.062 2.586-.034 3.876.607-.358 1.2-.741 1.798-1.114zm-13.804-.784c.06-1.06.19-2.269-1.09-2.583-.807-.376-1.926-1.341-2.548-1.332-.02 1.195-.01 2.39-.011 3.585 1.192.744 2.364 1.524 3.582 2.226.119-.616.041-1.269.067-1.896zm8.541 4.105l2.117-1.336c-.003-1.284.05-2.57-.008-3.853-.776.223-1.662.91-2.48 1.337l-1.834 1.075c.012 1.37-.033 2.744.044 4.113.732-.427 1.443-.887 2.161-1.336zm-2.957-.72v-2.057c-1.416-.828-2.828-1.664-4.25-2.482-.078 1.311-.033 2.627-.045 3.94 1.416.887 2.817 1.798 4.25 2.655.057-.683.036-1.372.045-2.057zm8.255 2.755l1.731-1.153c-.024-1.218.06-2.453-.062-3.658-1.2.685-2.358 1.464-3.537 2.195.028 1.261-.058 2.536.072 3.786.609-.373 1.2-.777 1.796-1.17zm-13.851-.683l-.014-1.916c-1.193-.746-2.37-1.517-3.58-2.234-.076 1.224-.033 2.453-.044 3.679 1.203.796 2.392 1.614 3.61 2.385.048-.636.024-1.276.028-1.914zm8.584 4.199l2.102-1.396c-.002-1.298.024-2.596-.01-3.893-1.427.88-2.843 1.775-4.25 2.686-.158 1.253-.055 2.545-.056 3.811.437.266 1.553-.912 2.214-1.208zm-2.988-.556c-.085-.894.365-2.154-.773-2.5-1.146-.727-2.288-1.46-3.45-2.163-.17 1.228.008 2.508-.122 3.751a79.399 79.399 0 0 0 4.278 2.885c.117-.641.044-1.32.067-1.973zm-4.872-.236l-5.087-3.396c.002-3.493-.047-6.988.015-10.48.85-.524 1.753-.954 2.627-1.434-.564-1.616.25-3.58 1.887-4.184 1.372-.563 3.025-.055 3.9 1.13l1.906-.978 1.916.987c.915-1.086 2.483-1.706 3.842-1.097 1.631.573 2.52 2.532 1.936 4.145.88.497 1.837.886 2.644 1.492.036 3.473 0 6.946-.003 10.419-3.374 2.233-6.693 4.55-10.122 6.699-.997 0-1.858-1.083-2.783-1.522a735.316 735.316 0 0 1-2.678-1.781z';
- private const GHOST_HEART = 'M125.914 8.305c3.036-8.71 14.933 0 0 11.2-14.932-11.2-3.036-19.91 0-11.2z';
- private const GHOST_PLUS = 'M111.368 8.97h7.324V1.645h7.512v7.323h7.324v7.513h-7.324v7.323h-7.512v-7.323h-7.324z';
-
- private $debug;
- private $charset;
- private $handler;
- private $caughtBuffer;
- private $caughtLength;
- private $fileLinkFormat;
-
- public function __construct(bool $debug = true, string $charset = null, $fileLinkFormat = null)
- {
- $this->debug = $debug;
- $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8';
- $this->fileLinkFormat = $fileLinkFormat;
- }
-
- /**
- * Registers the exception handler.
- *
- * @param bool $debug Enable/disable debug mode, where the stack trace is displayed
- * @param string|null $charset The charset used by exception messages
- * @param string|null $fileLinkFormat The IDE link template
- *
- * @return static
- */
- public static function register($debug = true, $charset = null, $fileLinkFormat = null)
- {
- $handler = new static($debug, $charset, $fileLinkFormat);
-
- $prev = set_exception_handler([$handler, 'handle']);
- if (\is_array($prev) && $prev[0] instanceof ErrorHandler) {
- restore_exception_handler();
- $prev[0]->setExceptionHandler([$handler, 'handle']);
- }
-
- return $handler;
- }
-
- /**
- * Sets a user exception handler.
- *
- * @param callable $handler An handler that will be called on Exception
- *
- * @return callable|null The previous exception handler if any
- */
- public function setHandler(callable $handler = null)
- {
- $old = $this->handler;
- $this->handler = $handler;
-
- return $old;
- }
-
- /**
- * Sets the format for links to source files.
- *
- * @param string|FileLinkFormatter $fileLinkFormat The format for links to source files
- *
- * @return string The previous file link format
- */
- public function setFileLinkFormat($fileLinkFormat)
- {
- $old = $this->fileLinkFormat;
- $this->fileLinkFormat = $fileLinkFormat;
-
- return $old;
- }
-
- /**
- * Sends a response for the given Exception.
- *
- * To be as fail-safe as possible, the exception is first handled
- * by our simple exception handler, then by the user exception handler.
- * The latter takes precedence and any output from the former is cancelled,
- * if and only if nothing bad happens in this handling path.
- */
- public function handle(\Exception $exception)
- {
- if (null === $this->handler || $exception instanceof OutOfMemoryException) {
- $this->sendPhpResponse($exception);
-
- return;
- }
-
- $caughtLength = $this->caughtLength = 0;
-
- ob_start(function ($buffer) {
- $this->caughtBuffer = $buffer;
-
- return '';
- });
-
- $this->sendPhpResponse($exception);
- while (null === $this->caughtBuffer && ob_end_flush()) {
- // Empty loop, everything is in the condition
- }
- if (isset($this->caughtBuffer[0])) {
- ob_start(function ($buffer) {
- if ($this->caughtLength) {
- // use substr_replace() instead of substr() for mbstring overloading resistance
- $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength);
- if (isset($cleanBuffer[0])) {
- $buffer = $cleanBuffer;
- }
- }
-
- return $buffer;
- });
-
- echo $this->caughtBuffer;
- $caughtLength = ob_get_length();
- }
- $this->caughtBuffer = null;
-
- try {
- ($this->handler)($exception);
- $this->caughtLength = $caughtLength;
- } catch (\Exception $e) {
- if (!$caughtLength) {
- // All handlers failed. Let PHP handle that now.
- throw $exception;
- }
- }
- }
-
- /**
- * Sends the error associated with the given Exception as a plain PHP response.
- *
- * This method uses plain PHP functions like header() and echo to output
- * the response.
- *
- * @param \Throwable|FlattenException $exception A \Throwable or FlattenException instance
- */
- public function sendPhpResponse($exception)
- {
- if ($exception instanceof \Throwable) {
- $exception = FlattenException::createFromThrowable($exception);
- }
-
- if (!headers_sent()) {
- header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
- foreach ($exception->getHeaders() as $name => $value) {
- header($name.': '.$value, false);
- }
- header('Content-Type: text/html; charset='.$this->charset);
- }
-
- echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
- }
-
- /**
- * Gets the full HTML content associated with the given exception.
- *
- * @param \Exception|FlattenException $exception An \Exception or FlattenException instance
- *
- * @return string The HTML content as a string
- */
- public function getHtml($exception)
- {
- if (!$exception instanceof FlattenException) {
- $exception = FlattenException::create($exception);
- }
-
- return $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
- }
-
- /**
- * Gets the HTML content associated with the given exception.
- *
- * @return string The content as a string
- */
- public function getContent(FlattenException $exception)
- {
- switch ($exception->getStatusCode()) {
- case 404:
- $title = 'Sorry, the page you are looking for could not be found.';
- break;
- default:
- $title = $this->debug ? $this->escapeHtml($exception->getMessage()) : 'Whoops, looks like something went wrong.';
- }
-
- if (!$this->debug) {
- return <<
- $title
-
-EOF;
- }
-
- $content = '';
- try {
- $count = \count($exception->getAllPrevious());
- $total = $count + 1;
- foreach ($exception->toArray() as $position => $e) {
- $ind = $count - $position + 1;
- $class = $this->formatClass($e['class']);
- $message = nl2br($this->escapeHtml($e['message']));
- $content .= sprintf(<<<'EOF'
-
-
-
-
- (%d/%d)
- %s
-
- %s
- |
-
-EOF
- , $ind, $total, $class, $message);
- foreach ($e['trace'] as $trace) {
- $content .= '';
- if ($trace['function']) {
- $content .= sprintf('at %s%s%s', $this->formatClass($trace['class']), $trace['type'], $trace['function']);
-
- if (isset($trace['args'])) {
- $content .= sprintf('(%s)', $this->formatArgs($trace['args']));
- }
- }
- if (isset($trace['file']) && isset($trace['line'])) {
- $content .= $this->formatPath($trace['file'], $trace['line']);
- }
- $content .= " |
\n";
- }
-
- $content .= "\n
\n
\n";
- }
- } catch (\Exception $e) {
- // something nasty happened and we cannot throw an exception anymore
- if ($this->debug) {
- $e = FlattenException::create($e);
- $title = sprintf('Exception thrown when handling an exception (%s: %s)', $e->getClass(), $this->escapeHtml($e->getMessage()));
- } else {
- $title = 'Whoops, looks like something went wrong.';
- }
- }
-
- $symfonyGhostImageContents = $this->getSymfonyGhostAsSvg();
-
- return <<
-
-
-
$title
-
$symfonyGhostImageContents
-
-
-
-
-
- $content
-
-EOF;
- }
-
- /**
- * Gets the stylesheet associated with the given exception.
- *
- * @return string The stylesheet as a string
- */
- public function getStylesheet(FlattenException $exception)
- {
- if (!$this->debug) {
- return <<<'EOF'
- body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; }
- .container { margin: 30px; max-width: 600px; }
- h1 { color: #dc3545; font-size: 24px; }
-EOF;
- }
-
- return <<<'EOF'
- body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; }
-
- a { cursor: pointer; text-decoration: none; }
- a:hover { text-decoration: underline; }
- abbr[title] { border-bottom: none; cursor: help; text-decoration: none; }
-
- code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; }
-
- table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; }
- table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
- table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; }
- table th { background-color: #E0E0E0; font-weight: bold; text-align: left; }
-
- .hidden-xs-down { display: none; }
- .block { display: block; }
- .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; }
- .text-muted { color: #999; }
-
- .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; }
- .container::after { content: ""; display: table; clear: both; }
-
- .exception-summary { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; }
-
- .exception-message-wrapper { display: flex; align-items: center; min-height: 70px; }
- .exception-message { flex-grow: 1; padding: 30px 0; }
- .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; }
- .exception-message.long { font-size: 18px; }
- .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; }
- .exception-message a:hover { border-bottom-color: #ffffff; }
-
- .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; }
-
- .trace + .trace { margin-top: 30px; }
- .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }
-
- .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }
-
- .trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; }
- .trace-class { color: #B0413E; }
- .trace-type { padding: 0 2px; }
- .trace-method { color: #B0413E; font-weight: bold; }
- .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; }
-
- @media (min-width: 575px) {
- .hidden-xs-down { display: initial; }
- }
-EOF;
- }
-
- private function decorate(string $content, string $css): string
- {
- return <<
-
-
-
-
-
-
-
- $content
-
-
-EOF;
- }
-
- private function formatClass(string $class): string
- {
- $parts = explode('\\', $class);
-
- return sprintf('%s', $class, array_pop($parts));
- }
-
- private function formatPath(string $path, int $line): string
- {
- $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path);
- $fmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
-
- if (!$fmt) {
- return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : '');
- }
-
- if (\is_string($fmt)) {
- $i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f);
- $fmt = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE);
-
- for ($i = 1; isset($fmt[$i]); ++$i) {
- if (0 === strpos($path, $k = $fmt[$i++])) {
- $path = substr_replace($path, $fmt[$i], 0, \strlen($k));
- break;
- }
- }
-
- $link = strtr($fmt[0], ['%f' => $path, '%l' => $line]);
- } else {
- try {
- $link = $fmt->format($path, $line);
- } catch (\Exception $e) {
- return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : '');
- }
- }
-
- return sprintf('in %s%s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$line : '');
- }
-
- /**
- * Formats an array as a string.
- */
- private function formatArgs(array $args): string
- {
- $result = [];
- foreach ($args as $key => $item) {
- if ('object' === $item[0]) {
- $formattedValue = sprintf('object(%s)', $this->formatClass($item[1]));
- } elseif ('array' === $item[0]) {
- $formattedValue = sprintf('array(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
- } elseif ('null' === $item[0]) {
- $formattedValue = 'null';
- } elseif ('boolean' === $item[0]) {
- $formattedValue = ''.strtolower(var_export($item[1], true)).'';
- } elseif ('resource' === $item[0]) {
- $formattedValue = 'resource';
- } else {
- $formattedValue = str_replace("\n", '', $this->escapeHtml(var_export($item[1], true)));
- }
-
- $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formattedValue);
- }
-
- return implode(', ', $result);
- }
-
- /**
- * HTML-encodes a string.
- */
- private function escapeHtml(string $str): string
- {
- return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset);
- }
-
- private function getSymfonyGhostAsSvg(): string
- {
- return '';
- }
-
- private function addElementToGhost(): string
- {
- if (!isset(self::GHOST_ADDONS[date('m-d')])) {
- return '';
- }
-
- return '';
- }
-}
diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php
deleted file mode 100644
index e15dfe48eb7af..0000000000000
--- a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php
+++ /dev/null
@@ -1,199 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\FatalErrorHandler;
-
-use Composer\Autoload\ClassLoader as ComposerClassLoader;
-use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
-use Symfony\Component\Debug\DebugClassLoader;
-use Symfony\Component\Debug\Exception\ClassNotFoundException;
-use Symfony\Component\Debug\Exception\FatalErrorException;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ClassNotFoundFatalErrorHandler::class, \Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler::class), E_USER_DEPRECATED);
-
-/**
- * ErrorHandler for classes that do not exist.
- *
- * @author Fabien Potencier
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler instead.
- */
-class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
-{
- /**
- * {@inheritdoc}
- */
- public function handleError(array $error, FatalErrorException $exception)
- {
- $messageLen = \strlen($error['message']);
- $notFoundSuffix = '\' not found';
- $notFoundSuffixLen = \strlen($notFoundSuffix);
- if ($notFoundSuffixLen > $messageLen) {
- return null;
- }
-
- if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
- return null;
- }
-
- foreach (['class', 'interface', 'trait'] as $typeName) {
- $prefix = ucfirst($typeName).' \'';
- $prefixLen = \strlen($prefix);
- if (0 !== strpos($error['message'], $prefix)) {
- continue;
- }
-
- $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
- if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
- $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
- $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
- $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix);
- $tail = ' for another namespace?';
- } else {
- $className = $fullyQualifiedClassName;
- $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className);
- $tail = '?';
- }
-
- if ($candidates = $this->getClassCandidates($className)) {
- $tail = array_pop($candidates).'"?';
- if ($candidates) {
- $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail;
- } else {
- $tail = ' for "'.$tail;
- }
- }
- $message .= "\nDid you forget a \"use\" statement".$tail;
-
- return new ClassNotFoundException($message, $exception);
- }
-
- return null;
- }
-
- /**
- * Tries to guess the full namespace for a given class name.
- *
- * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer
- * autoloader (that should cover all common cases).
- *
- * @param string $class A class name (without its namespace)
- *
- * @return array An array of possible fully qualified class names
- */
- private function getClassCandidates(string $class): array
- {
- if (!\is_array($functions = spl_autoload_functions())) {
- return [];
- }
-
- // find Symfony and Composer autoloaders
- $classes = [];
-
- foreach ($functions as $function) {
- if (!\is_array($function)) {
- continue;
- }
- // get class loaders wrapped by DebugClassLoader
- if ($function[0] instanceof DebugClassLoader) {
- $function = $function[0]->getClassLoader();
-
- if (!\is_array($function)) {
- continue;
- }
- }
-
- if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
- foreach ($function[0]->getPrefixes() as $prefix => $paths) {
- foreach ($paths as $path) {
- $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
- }
- }
- }
- if ($function[0] instanceof ComposerClassLoader) {
- foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) {
- foreach ($paths as $path) {
- $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
- }
- }
- }
- }
-
- return array_unique($classes);
- }
-
- private function findClassInPath(string $path, string $class, string $prefix): array
- {
- if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
- return [];
- }
-
- $classes = [];
- $filename = $class.'.php';
- foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
- if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) {
- $classes[] = $class;
- }
- }
-
- return $classes;
- }
-
- private function convertFileToClass(string $path, string $file, string $prefix): ?string
- {
- $candidates = [
- // namespaced class
- $namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $file),
- // namespaced class (with target dir)
- $prefix.$namespacedClass,
- // namespaced class (with target dir and separator)
- $prefix.'\\'.$namespacedClass,
- // PEAR class
- str_replace('\\', '_', $namespacedClass),
- // PEAR class (with target dir)
- str_replace('\\', '_', $prefix.$namespacedClass),
- // PEAR class (with target dir and separator)
- str_replace('\\', '_', $prefix.'\\'.$namespacedClass),
- ];
-
- if ($prefix) {
- $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); });
- }
-
- // We cannot use the autoloader here as most of them use require; but if the class
- // is not found, the new autoloader call will require the file again leading to a
- // "cannot redeclare class" error.
- foreach ($candidates as $candidate) {
- if ($this->classExists($candidate)) {
- return $candidate;
- }
- }
-
- try {
- require_once $file;
- } catch (\Throwable $e) {
- return null;
- }
-
- foreach ($candidates as $candidate) {
- if ($this->classExists($candidate)) {
- return $candidate;
- }
- }
-
- return null;
- }
-
- private function classExists(string $class): bool
- {
- return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
- }
-}
diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php
deleted file mode 100644
index fbf2ae00a60db..0000000000000
--- a/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\FatalErrorHandler;
-
-use Symfony\Component\Debug\Exception\FatalErrorException;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', FatalErrorHandlerInterface::class, \Symfony\Component\ErrorHandler\FatalErrorHandler\FatalErrorHandlerInterface::class), E_USER_DEPRECATED);
-
-/**
- * Attempts to convert fatal errors to exceptions.
- *
- * @author Fabien Potencier
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\FatalErrorHandler\FatalErrorHandlerInterface instead.
- */
-interface FatalErrorHandlerInterface
-{
- /**
- * Attempts to convert an error into an exception.
- *
- * @param array $error An array as returned by error_get_last()
- *
- * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise
- */
- public function handleError(array $error, FatalErrorException $exception);
-}
diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php
deleted file mode 100644
index 67b7ddd1610ee..0000000000000
--- a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php
+++ /dev/null
@@ -1,88 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\FatalErrorHandler;
-
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Symfony\Component\Debug\Exception\UndefinedFunctionException;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedFunctionFatalErrorHandler::class, \Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer::class), E_USER_DEPRECATED);
-
-/**
- * ErrorHandler for undefined functions.
- *
- * @author Fabien Potencier
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer instead.
- */
-class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
-{
- /**
- * {@inheritdoc}
- */
- public function handleError(array $error, FatalErrorException $exception)
- {
- $messageLen = \strlen($error['message']);
- $notFoundSuffix = '()';
- $notFoundSuffixLen = \strlen($notFoundSuffix);
- if ($notFoundSuffixLen > $messageLen) {
- return null;
- }
-
- if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
- return null;
- }
-
- $prefix = 'Call to undefined function ';
- $prefixLen = \strlen($prefix);
- if (0 !== strpos($error['message'], $prefix)) {
- return null;
- }
-
- $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
- if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) {
- $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1);
- $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex);
- $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix);
- } else {
- $functionName = $fullyQualifiedFunctionName;
- $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName);
- }
-
- $candidates = [];
- foreach (get_defined_functions() as $type => $definedFunctionNames) {
- foreach ($definedFunctionNames as $definedFunctionName) {
- if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) {
- $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1);
- } else {
- $definedFunctionNameBasename = $definedFunctionName;
- }
-
- if ($definedFunctionNameBasename === $functionName) {
- $candidates[] = '\\'.$definedFunctionName;
- }
- }
- }
-
- if ($candidates) {
- sort($candidates);
- $last = array_pop($candidates).'"?';
- if ($candidates) {
- $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last;
- } else {
- $candidates = '"'.$last;
- }
- $message .= "\nDid you mean to call ".$candidates;
- }
-
- return new UndefinedFunctionException($message, $exception);
- }
-}
diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php
deleted file mode 100644
index 8ee359ab27771..0000000000000
--- a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php
+++ /dev/null
@@ -1,70 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\FatalErrorHandler;
-
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Symfony\Component\Debug\Exception\UndefinedMethodException;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', UndefinedMethodFatalErrorHandler::class, \Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer::class), E_USER_DEPRECATED);
-
-/**
- * ErrorHandler for undefined methods.
- *
- * @author Grégoire Pineau
- *
- * @deprecated since Symfony 4.4, use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer instead.
- */
-class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
-{
- /**
- * {@inheritdoc}
- */
- public function handleError(array $error, FatalErrorException $exception)
- {
- preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches);
- if (!$matches) {
- return null;
- }
-
- $className = $matches[1];
- $methodName = $matches[2];
-
- $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className);
-
- if (!class_exists($className) || null === $methods = get_class_methods($className)) {
- // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class)
- return new UndefinedMethodException($message, $exception);
- }
-
- $candidates = [];
- foreach ($methods as $definedMethodName) {
- $lev = levenshtein($methodName, $definedMethodName);
- if ($lev <= \strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) {
- $candidates[] = $definedMethodName;
- }
- }
-
- if ($candidates) {
- sort($candidates);
- $last = array_pop($candidates).'"?';
- if ($candidates) {
- $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last;
- } else {
- $candidates = '"'.$last;
- }
-
- $message .= "\nDid you mean to call ".$candidates;
- }
-
- return new UndefinedMethodException($message, $exception);
- }
-}
diff --git a/src/Symfony/Component/Debug/LICENSE b/src/Symfony/Component/Debug/LICENSE
deleted file mode 100644
index 9e936ec0448b8..0000000000000
--- a/src/Symfony/Component/Debug/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2004-2020 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/src/Symfony/Component/Debug/README.md b/src/Symfony/Component/Debug/README.md
deleted file mode 100644
index 90b36f0cb8e59..0000000000000
--- a/src/Symfony/Component/Debug/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-Debug Component
-===============
-
-**CAUTION**: this component is deprecated since Symfony 4.4. Instead, use the
-[ErrorHandler component](https://github.com/symfony/symfony/tree/master/src/Symfony/Component/ErrorHandler).
-
------
-
-The Debug component provides tools to ease debugging PHP code.
-
-Getting Started
----------------
-
-```
-$ composer install symfony/debug
-```
-
-```php
-use Symfony\Component\Debug\Debug;
-
-Debug::enable();
-```
-
-Resources
----------
-
- * [Contributing](https://symfony.com/doc/current/contributing/index.html)
- * [Report issues](https://github.com/symfony/symfony/issues) and
- [send Pull Requests](https://github.com/symfony/symfony/pulls)
- in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
deleted file mode 100644
index c946607959f6e..0000000000000
--- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
+++ /dev/null
@@ -1,447 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\DebugClassLoader;
-
-/**
- * @group legacy
- */
-class DebugClassLoaderTest extends TestCase
-{
- /**
- * @var int Error reporting level before running tests
- */
- private $errorReporting;
-
- private $loader;
-
- protected function setUp(): void
- {
- $this->errorReporting = error_reporting(E_ALL);
- $this->loader = [new DebugClassLoader([new ClassLoader(), 'loadClass']), 'loadClass'];
- spl_autoload_register($this->loader, true, true);
- }
-
- protected function tearDown(): void
- {
- spl_autoload_unregister($this->loader);
- error_reporting($this->errorReporting);
- }
-
- /**
- * @runInSeparateProcess
- */
- public function testIdempotence()
- {
- DebugClassLoader::enable();
- DebugClassLoader::enable();
-
- $functions = spl_autoload_functions();
- foreach ($functions as $function) {
- if (\is_array($function) && $function[0] instanceof DebugClassLoader) {
- $reflClass = new \ReflectionClass($function[0]);
- $reflProp = $reflClass->getProperty('classLoader');
- $reflProp->setAccessible(true);
-
- $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0]));
-
- return;
- }
- }
-
- $this->fail('DebugClassLoader did not register');
- }
-
- public function testThrowingClass()
- {
- $this->expectException('Exception');
- $this->expectExceptionMessage('boo');
- try {
- class_exists(Fixtures\Throwing::class);
- $this->fail('Exception expected');
- } catch (\Exception $e) {
- $this->assertSame('boo', $e->getMessage());
- }
-
- // the second call also should throw
- class_exists(Fixtures\Throwing::class);
- }
-
- public function testNameCaseMismatch()
- {
- $this->expectException('RuntimeException');
- $this->expectExceptionMessage('Case mismatch between loaded and declared class names');
- class_exists(TestingCaseMismatch::class, true);
- }
-
- public function testFileCaseMismatch()
- {
- $this->expectException('RuntimeException');
- $this->expectExceptionMessage('Case mismatch between class and real file names');
- if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) {
- $this->markTestSkipped('Can only be run on case insensitive filesystems');
- }
-
- class_exists(Fixtures\CaseMismatch::class, true);
- }
-
- public function testPsr4CaseMismatch()
- {
- $this->expectException('RuntimeException');
- $this->expectExceptionMessage('Case mismatch between loaded and declared class names');
- class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true);
- }
-
- public function testNotPsr0()
- {
- $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true));
- }
-
- public function testNotPsr0Bis()
- {
- $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true));
- }
-
- public function testClassAlias()
- {
- $this->assertTrue(class_exists(Fixtures\ClassAlias::class, true));
- }
-
- /**
- * @dataProvider provideDeprecatedSuper
- */
- public function testDeprecatedSuper($class, $super, $type)
- {
- set_error_handler(function () { return false; });
- $e = error_reporting(0);
- trigger_error('', E_USER_DEPRECATED);
-
- class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $lastError = error_get_last();
- unset($lastError['file'], $lastError['line']);
-
- $xError = [
- 'type' => E_USER_DEPRECATED,
- 'message' => 'The "Test\Symfony\Component\Debug\Tests\\'.$class.'" class '.$type.' "Symfony\Component\Debug\Tests\Fixtures\\'.$super.'" that is deprecated but this is a test deprecation notice.',
- ];
-
- $this->assertSame($xError, $lastError);
- }
-
- public function provideDeprecatedSuper()
- {
- return [
- ['DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'],
- ['DeprecatedParentClass', 'DeprecatedClass', 'extends'],
- ];
- }
-
- public function testInterfaceExtendsDeprecatedInterface()
- {
- set_error_handler(function () { return false; });
- $e = error_reporting(0);
- trigger_error('', E_USER_NOTICE);
-
- class_exists('Test\\'.NonDeprecatedInterfaceClass::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $lastError = error_get_last();
- unset($lastError['file'], $lastError['line']);
-
- $xError = [
- 'type' => E_USER_NOTICE,
- 'message' => '',
- ];
-
- $this->assertSame($xError, $lastError);
- }
-
- public function testDeprecatedSuperInSameNamespace()
- {
- set_error_handler(function () { return false; });
- $e = error_reporting(0);
- trigger_error('', E_USER_NOTICE);
-
- class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true);
-
- error_reporting($e);
- restore_error_handler();
-
- $lastError = error_get_last();
- unset($lastError['file'], $lastError['line']);
-
- $xError = [
- 'type' => E_USER_NOTICE,
- 'message' => '',
- ];
-
- $this->assertSame($xError, $lastError);
- }
-
- public function testExtendedFinalClass()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- require __DIR__.'/Fixtures/FinalClasses.php';
-
- $i = 1;
- while (class_exists($finalClass = Fixtures\FinalClass::class.$i++, false)) {
- spl_autoload_call($finalClass);
- class_exists('Test\\'.__NAMESPACE__.'\\Extends'.substr($finalClass, strrpos($finalClass, '\\') + 1), true);
- }
-
- error_reporting($e);
- restore_error_handler();
-
- $this->assertSame([
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass1" class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass1".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass2" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass2".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass3" class is considered final comment with @@@ and ***. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass3".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass4" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass4".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass5" class is considered final multiline comment. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass5".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass6" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass6".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass7" class is considered final another multiline comment... It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass7".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass8" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass8".',
- ], $deprecations);
- }
-
- public function testExtendedFinalMethod()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- class_exists(Fixtures\ExtendedFinalMethod::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $xError = [
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
- ];
-
- $this->assertSame($xError, $deprecations);
- }
-
- public function testExtendedDeprecatedMethodDoesntTriggerAnyNotice()
- {
- set_error_handler(function () { return false; });
- $e = error_reporting(0);
- trigger_error('', E_USER_NOTICE);
-
- class_exists('Test\\'.ExtendsAnnotatedClass::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $lastError = error_get_last();
- unset($lastError['file'], $lastError['line']);
-
- $this->assertSame(['type' => E_USER_NOTICE, 'message' => ''], $lastError);
- }
-
- public function testInternalsUse()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- class_exists('Test\\'.ExtendsInternals::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $this->assertSame($deprecations, [
- 'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
- 'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass::internalMethod()" method is considered internal. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
- ]);
- }
-
- public function testExtendedMethodDefinesNewParameters()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- class_exists(Fixtures\SubClassWithAnnotatedParameters::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $this->assertSame([
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
- 'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.',
- ], $deprecations);
- }
-
- public function testUseTraitWithInternalMethod()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- class_exists('Test\\'.UseTraitWithInternalMethod::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $this->assertSame([], $deprecations);
- }
-
- public function testVirtualUse()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- class_exists('Test\\'.ExtendsVirtual::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $this->assertSame([
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::sameLineInterfaceMethodNoBraces()".',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::newLineInterfaceMethod()": Some description!',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::newLineInterfaceMethodNoBraces()": Description.',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::invalidInterfaceMethod()".',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::invalidInterfaceMethodNoBraces()".',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::complexInterfaceMethod($arg, ...$args)".',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::complexInterfaceMethodTyped($arg, int ...$args)": Description ...',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodNoBraces()".',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodTyped(int $arg)": Description.',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodTypedNoBraces()".',
- 'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtual" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualSubInterface::subInterfaceMethod()".',
- ], $deprecations);
- }
-
- public function testVirtualUseWithMagicCall()
- {
- $deprecations = [];
- set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
- $e = error_reporting(E_USER_DEPRECATED);
-
- class_exists('Test\\'.ExtendsVirtualMagicCall::class, true);
-
- error_reporting($e);
- restore_error_handler();
-
- $this->assertSame([], $deprecations);
- }
-
- public function testEvaluatedCode()
- {
- $this->assertTrue(class_exists(Fixtures\DefinitionInEvaluatedCode::class, true));
- }
-}
-
-class ClassLoader
-{
- public function loadClass($class)
- {
- }
-
- public function getClassMap()
- {
- return [__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'];
- }
-
- public function findFile($class)
- {
- $fixtureDir = __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR;
-
- if (TestingUnsilencing::class === $class) {
- eval('-- parse error --');
- } elseif (TestingStacking::class === $class) {
- eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }');
- } elseif (TestingCaseMismatch::class === $class) {
- eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
- } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
- return $fixtureDir.'psr4'.\DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
- } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
- return $fixtureDir.'reallyNotPsr0.php';
- } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
- return $fixtureDir.'notPsr0Bis.php';
- } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) {
- eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
- } elseif ('Test\\'.DeprecatedParentClass::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
- } elseif ('Test\\'.DeprecatedInterfaceClass::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}');
- } elseif ('Test\\'.NonDeprecatedInterfaceClass::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
- } elseif ('Test\\'.Float::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
- } elseif (0 === strpos($class, 'Test\\'.ExtendsFinalClass::class)) {
- $classShortName = substr($class, strrpos($class, '\\') + 1);
- eval('namespace Test\\'.__NAMESPACE__.'; class '.$classShortName.' extends \\'.__NAMESPACE__.'\Fixtures\\'.substr($classShortName, 7).' {}');
- } elseif ('Test\\'.ExtendsAnnotatedClass::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass {
- public function deprecatedMethod() { }
- }');
- } elseif ('Test\\'.ExtendsInternals::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent {
- use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
-
- public function internalMethod() { }
- }');
- } elseif ('Test\\'.ExtendsInternalsParent::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
- } elseif ('Test\\'.UseTraitWithInternalMethod::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }');
- } elseif ('Test\\'.ExtendsVirtual::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtual extends ExtendsVirtualParent implements \\'.__NAMESPACE__.'\Fixtures\VirtualSubInterface {
- public function ownClassMethod() { }
- public function classMethod() { }
- public function sameLineInterfaceMethodNoBraces() { }
- }');
- } elseif ('Test\\'.ExtendsVirtualParent::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualParent extends ExtendsVirtualAbstract {
- public function ownParentMethod() { }
- public function traitMethod() { }
- public function sameLineInterfaceMethod() { }
- public function staticMethodNoBraces() { } // should be static
- }');
- } elseif ('Test\\'.ExtendsVirtualAbstract::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstract extends ExtendsVirtualAbstractBase {
- public static function staticMethod() { }
- public function ownAbstractMethod() { }
- public function interfaceMethod() { }
- }');
- } elseif ('Test\\'.ExtendsVirtualAbstractBase::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstractBase extends \\'.__NAMESPACE__.'\Fixtures\VirtualClass implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
- public function ownAbstractBaseMethod() { }
- }');
- } elseif ('Test\\'.ExtendsVirtualMagicCall::class === $class) {
- eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualMagicCall extends \\'.__NAMESPACE__.'\Fixtures\VirtualClassMagicCall implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
- }');
- }
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
deleted file mode 100644
index b45a6d08c5b64..0000000000000
--- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
+++ /dev/null
@@ -1,564 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Psr\Log\LogLevel;
-use Psr\Log\NullLogger;
-use Symfony\Component\Debug\BufferingLogger;
-use Symfony\Component\Debug\ErrorHandler;
-use Symfony\Component\Debug\Exception\SilencedErrorContext;
-use Symfony\Component\Debug\Tests\Fixtures\ErrorHandlerThatUsesThePreviousOne;
-use Symfony\Component\Debug\Tests\Fixtures\LoggerThatSetAnErrorHandler;
-
-/**
- * ErrorHandlerTest.
- *
- * @author Robert Schönthal
- * @author Nicolas Grekas
- *
- * @group legacy
- */
-class ErrorHandlerTest extends TestCase
-{
- public function testRegister()
- {
- $handler = ErrorHandler::register();
-
- try {
- $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler);
- $this->assertSame($handler, ErrorHandler::register());
-
- $newHandler = new ErrorHandler();
-
- $this->assertSame($handler, ErrorHandler::register($newHandler, false));
- $h = set_error_handler('var_dump');
- restore_error_handler();
- $this->assertSame([$handler, 'handleError'], $h);
-
- try {
- $this->assertSame($newHandler, ErrorHandler::register($newHandler, true));
- $h = set_error_handler('var_dump');
- restore_error_handler();
- $this->assertSame([$newHandler, 'handleError'], $h);
- } catch (\Exception $e) {
- }
-
- restore_error_handler();
- restore_exception_handler();
-
- if (isset($e)) {
- throw $e;
- }
- } catch (\Exception $e) {
- }
-
- restore_error_handler();
- restore_exception_handler();
-
- if (isset($e)) {
- throw $e;
- }
- }
-
- public function testErrorGetLast()
- {
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $handler = ErrorHandler::register();
- $handler->setDefaultLogger($logger);
- $handler->screamAt(E_ALL);
-
- try {
- @trigger_error('Hello', E_USER_WARNING);
- $expected = [
- 'type' => E_USER_WARNING,
- 'message' => 'Hello',
- 'file' => __FILE__,
- 'line' => __LINE__ - 5,
- ];
- $this->assertSame($expected, error_get_last());
- } catch (\Exception $e) {
- restore_error_handler();
- restore_exception_handler();
-
- throw $e;
- }
- }
-
- public function testNotice()
- {
- ErrorHandler::register();
-
- try {
- self::triggerNotice($this);
- $this->fail('ErrorException expected');
- } catch (\ErrorException $exception) {
- // if an exception is thrown, the test passed
- $this->assertEquals(E_NOTICE, $exception->getSeverity());
- $this->assertEquals(__FILE__, $exception->getFile());
- $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
-
- $trace = $exception->getTrace();
-
- $this->assertEquals(__FILE__, $trace[0]['file']);
- $this->assertEquals(__CLASS__, $trace[0]['class']);
- $this->assertEquals('triggerNotice', $trace[0]['function']);
- $this->assertEquals('::', $trace[0]['type']);
-
- $this->assertEquals(__FILE__, $trace[0]['file']);
- $this->assertEquals(__CLASS__, $trace[1]['class']);
- $this->assertEquals(__FUNCTION__, $trace[1]['function']);
- $this->assertEquals('->', $trace[1]['type']);
- } finally {
- restore_error_handler();
- restore_exception_handler();
- }
- }
-
- // dummy function to test trace in error handler.
- private static function triggerNotice($that)
- {
- $that->assertSame('', $foo.$foo.$bar);
- }
-
- public function testConstruct()
- {
- try {
- $handler = ErrorHandler::register();
- $handler->throwAt(3, true);
- $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
- } finally {
- restore_error_handler();
- restore_exception_handler();
- }
- }
-
- public function testDefaultLogger()
- {
- try {
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $handler = ErrorHandler::register();
-
- $handler->setDefaultLogger($logger, E_NOTICE);
- $handler->setDefaultLogger($logger, [E_USER_NOTICE => LogLevel::CRITICAL]);
-
- $loggers = [
- E_DEPRECATED => [null, LogLevel::INFO],
- E_USER_DEPRECATED => [null, LogLevel::INFO],
- E_NOTICE => [$logger, LogLevel::WARNING],
- E_USER_NOTICE => [$logger, LogLevel::CRITICAL],
- E_STRICT => [null, LogLevel::WARNING],
- E_WARNING => [null, LogLevel::WARNING],
- E_USER_WARNING => [null, LogLevel::WARNING],
- E_COMPILE_WARNING => [null, LogLevel::WARNING],
- E_CORE_WARNING => [null, LogLevel::WARNING],
- E_USER_ERROR => [null, LogLevel::CRITICAL],
- E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
- E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
- E_PARSE => [null, LogLevel::CRITICAL],
- E_ERROR => [null, LogLevel::CRITICAL],
- E_CORE_ERROR => [null, LogLevel::CRITICAL],
- ];
- $this->assertSame($loggers, $handler->setLoggers([]));
- } finally {
- restore_error_handler();
- restore_exception_handler();
- }
- }
-
- public function testHandleError()
- {
- try {
- $handler = ErrorHandler::register();
- $handler->throwAt(0, true);
- $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, []));
-
- restore_error_handler();
- restore_exception_handler();
-
- $handler = ErrorHandler::register();
- $handler->throwAt(3, true);
- $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, []));
-
- restore_error_handler();
- restore_exception_handler();
-
- $handler = ErrorHandler::register();
- $handler->throwAt(3, true);
- try {
- $handler->handleError(4, 'foo', 'foo.php', 12, []);
- } catch (\ErrorException $e) {
- $this->assertSame('Parse Error: foo', $e->getMessage());
- $this->assertSame(4, $e->getSeverity());
- $this->assertSame('foo.php', $e->getFile());
- $this->assertSame(12, $e->getLine());
- }
-
- restore_error_handler();
- restore_exception_handler();
-
- $handler = ErrorHandler::register();
- $handler->throwAt(E_USER_DEPRECATED, true);
- $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
-
- restore_error_handler();
- restore_exception_handler();
-
- $handler = ErrorHandler::register();
- $handler->throwAt(E_DEPRECATED, true);
- $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, []));
-
- restore_error_handler();
- restore_exception_handler();
-
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
-
- $warnArgCheck = function ($logLevel, $message, $context) {
- $this->assertEquals('info', $logLevel);
- $this->assertEquals('User Deprecated: foo', $message);
- $this->assertArrayHasKey('exception', $context);
- $exception = $context['exception'];
- $this->assertInstanceOf(\ErrorException::class, $exception);
- $this->assertSame('User Deprecated: foo', $exception->getMessage());
- $this->assertSame(E_USER_DEPRECATED, $exception->getSeverity());
- };
-
- $logger
- ->expects($this->once())
- ->method('log')
- ->willReturnCallback($warnArgCheck)
- ;
-
- $handler = ErrorHandler::register();
- $handler->setDefaultLogger($logger, E_USER_DEPRECATED);
- $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
-
- restore_error_handler();
- restore_exception_handler();
-
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
-
- $line = null;
- $logArgCheck = function ($level, $message, $context) use (&$line) {
- $this->assertEquals('Notice: Undefined variable: undefVar', $message);
- $this->assertArrayHasKey('exception', $context);
- $exception = $context['exception'];
- $this->assertInstanceOf(SilencedErrorContext::class, $exception);
- $this->assertSame(E_NOTICE, $exception->getSeverity());
- $this->assertSame(__FILE__, $exception->getFile());
- $this->assertSame($line, $exception->getLine());
- $this->assertNotEmpty($exception->getTrace());
- $this->assertSame(1, $exception->count);
- };
-
- $logger
- ->expects($this->once())
- ->method('log')
- ->willReturnCallback($logArgCheck)
- ;
-
- $handler = ErrorHandler::register();
- $handler->setDefaultLogger($logger, E_NOTICE);
- $handler->screamAt(E_NOTICE);
- unset($undefVar);
- $line = __LINE__ + 1;
- @$undefVar++;
-
- restore_error_handler();
- restore_exception_handler();
- } catch (\Exception $e) {
- restore_error_handler();
- restore_exception_handler();
-
- throw $e;
- }
- }
-
- public function testHandleUserError()
- {
- if (\PHP_VERSION_ID >= 70400) {
- $this->markTestSkipped('PHP 7.4 allows __toString to throw exceptions');
- }
-
- try {
- $handler = ErrorHandler::register();
- $handler->throwAt(0, true);
-
- $e = null;
- $x = new \Exception('Foo');
-
- try {
- $f = new Fixtures\ToStringThrower($x);
- $f .= ''; // Trigger $f->__toString()
- } catch (\Exception $e) {
- }
-
- $this->assertSame($x, $e);
- } finally {
- restore_error_handler();
- restore_exception_handler();
- }
- }
-
- public function testHandleDeprecation()
- {
- $logArgCheck = function ($level, $message, $context) {
- $this->assertEquals(LogLevel::INFO, $level);
- $this->assertArrayHasKey('exception', $context);
- $exception = $context['exception'];
- $this->assertInstanceOf(\ErrorException::class, $exception);
- $this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage());
- };
-
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $logger
- ->expects($this->once())
- ->method('log')
- ->willReturnCallback($logArgCheck)
- ;
-
- $handler = new ErrorHandler();
- $handler->setDefaultLogger($logger);
- @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []);
-
- restore_error_handler();
- }
-
- public function testHandleException()
- {
- try {
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $handler = ErrorHandler::register();
-
- $exception = new \Exception('foo');
-
- $logArgCheck = function ($level, $message, $context) {
- $this->assertSame('Uncaught Exception: foo', $message);
- $this->assertArrayHasKey('exception', $context);
- $this->assertInstanceOf(\Exception::class, $context['exception']);
- };
-
- $logger
- ->expects($this->exactly(2))
- ->method('log')
- ->willReturnCallback($logArgCheck)
- ;
-
- $handler->setDefaultLogger($logger, E_ERROR);
-
- try {
- $handler->handleException($exception);
- $this->fail('Exception expected');
- } catch (\Exception $e) {
- $this->assertSame($exception, $e);
- }
-
- $handler->setExceptionHandler(function ($e) use ($exception) {
- $this->assertSame($exception, $e);
- });
-
- $handler->handleException($exception);
- } finally {
- restore_error_handler();
- restore_exception_handler();
- }
- }
-
- public function testBootstrappingLogger()
- {
- $bootLogger = new BufferingLogger();
- $handler = new ErrorHandler($bootLogger);
-
- $loggers = [
- E_DEPRECATED => [$bootLogger, LogLevel::INFO],
- E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO],
- E_NOTICE => [$bootLogger, LogLevel::WARNING],
- E_USER_NOTICE => [$bootLogger, LogLevel::WARNING],
- E_STRICT => [$bootLogger, LogLevel::WARNING],
- E_WARNING => [$bootLogger, LogLevel::WARNING],
- E_USER_WARNING => [$bootLogger, LogLevel::WARNING],
- E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING],
- E_CORE_WARNING => [$bootLogger, LogLevel::WARNING],
- E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL],
- E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL],
- E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL],
- E_PARSE => [$bootLogger, LogLevel::CRITICAL],
- E_ERROR => [$bootLogger, LogLevel::CRITICAL],
- E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL],
- ];
-
- $this->assertSame($loggers, $handler->setLoggers([]));
-
- $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, []);
-
- $logs = $bootLogger->cleanLogs();
-
- $this->assertCount(1, $logs);
- $log = $logs[0];
- $this->assertSame('info', $log[0]);
- $this->assertSame('Deprecated: Foo message', $log[1]);
- $this->assertArrayHasKey('exception', $log[2]);
- $exception = $log[2]['exception'];
- $this->assertInstanceOf(\ErrorException::class, $exception);
- $this->assertSame('Deprecated: Foo message', $exception->getMessage());
- $this->assertSame(__FILE__, $exception->getFile());
- $this->assertSame(123, $exception->getLine());
- $this->assertSame(E_DEPRECATED, $exception->getSeverity());
-
- $bootLogger->log(LogLevel::WARNING, 'Foo message', ['exception' => $exception]);
-
- $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $mockLogger->expects($this->once())
- ->method('log')
- ->with(LogLevel::WARNING, 'Foo message', ['exception' => $exception]);
-
- $handler->setLoggers([E_DEPRECATED => [$mockLogger, LogLevel::WARNING]]);
- }
-
- public function testSettingLoggerWhenExceptionIsBuffered()
- {
- $bootLogger = new BufferingLogger();
- $handler = new ErrorHandler($bootLogger);
-
- $exception = new \Exception('Foo message');
-
- $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $mockLogger->expects($this->once())
- ->method('log')
- ->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', ['exception' => $exception]);
-
- $handler->setExceptionHandler(function () use ($handler, $mockLogger) {
- $handler->setDefaultLogger($mockLogger);
- });
-
- $handler->handleException($exception);
- }
-
- public function testHandleFatalError()
- {
- try {
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
- $handler = ErrorHandler::register();
-
- $error = [
- 'type' => E_PARSE,
- 'message' => 'foo',
- 'file' => 'bar',
- 'line' => 123,
- ];
-
- $logArgCheck = function ($level, $message, $context) {
- $this->assertEquals('Fatal Parse Error: foo', $message);
- $this->assertArrayHasKey('exception', $context);
- $this->assertInstanceOf(\Exception::class, $context['exception']);
- };
-
- $logger
- ->expects($this->once())
- ->method('log')
- ->willReturnCallback($logArgCheck)
- ;
-
- $handler->setDefaultLogger($logger, E_PARSE);
-
- $handler->handleFatalError($error);
-
- restore_error_handler();
- restore_exception_handler();
- } catch (\Exception $e) {
- restore_error_handler();
- restore_exception_handler();
-
- throw $e;
- }
- }
-
- public function testHandleErrorException()
- {
- $exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found");
-
- $handler = new ErrorHandler();
- $handler->setExceptionHandler(function () use (&$args) {
- $args = \func_get_args();
- });
-
- $handler->handleException($exception);
-
- $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
- $this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
- }
-
- public function testCustomExceptionHandler()
- {
- $this->expectException('Exception');
- $handler = new ErrorHandler();
- $handler->setExceptionHandler(function ($e) use ($handler) {
- $handler->handleException($e);
- });
-
- $handler->handleException(new \Exception());
- }
-
- /**
- * @dataProvider errorHandlerWhenLoggingProvider
- */
- public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined)
- {
- try {
- if ($previousHandlerWasDefined) {
- set_error_handler('count');
- }
-
- $logger = $loggerSetsAnotherHandler ? new LoggerThatSetAnErrorHandler() : new NullLogger();
-
- $handler = ErrorHandler::register();
- $handler->setDefaultLogger($logger);
-
- if ($nextHandlerIsDefined) {
- $handler = ErrorHandlerThatUsesThePreviousOne::register();
- }
-
- @trigger_error('foo', E_USER_DEPRECATED);
- @trigger_error('bar', E_USER_DEPRECATED);
-
- $this->assertSame([$handler, 'handleError'], set_error_handler('var_dump'));
-
- if ($logger instanceof LoggerThatSetAnErrorHandler) {
- $this->assertCount(2, $logger->cleanLogs());
- }
-
- restore_error_handler();
-
- if ($previousHandlerWasDefined) {
- restore_error_handler();
- }
-
- if ($nextHandlerIsDefined) {
- restore_error_handler();
- }
- } finally {
- restore_error_handler();
- restore_exception_handler();
- }
- }
-
- public function errorHandlerWhenLoggingProvider()
- {
- foreach ([false, true] as $previousHandlerWasDefined) {
- foreach ([false, true] as $loggerSetsAnotherHandler) {
- foreach ([false, true] as $nextHandlerIsDefined) {
- yield [$previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined];
- }
- }
- }
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php b/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php
deleted file mode 100644
index a2620b2e28333..0000000000000
--- a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php
+++ /dev/null
@@ -1,403 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests\Exception;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\Exception\FatalThrowableError;
-use Symfony\Component\Debug\Exception\FlattenException;
-use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
-use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
-use Symfony\Component\HttpKernel\Exception\GoneHttpException;
-use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException;
-use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
-use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException;
-use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException;
-use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
-use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
-use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
-use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
-
-/**
- * @group legacy
- */
-class FlattenExceptionTest extends TestCase
-{
- public function testStatusCode()
- {
- $flattened = FlattenException::create(new \RuntimeException(), 403);
- $this->assertEquals('403', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new \RuntimeException());
- $this->assertEquals('500', $flattened->getStatusCode());
-
- $flattened = FlattenException::createFromThrowable(new \DivisionByZeroError(), 403);
- $this->assertEquals('403', $flattened->getStatusCode());
-
- $flattened = FlattenException::createFromThrowable(new \DivisionByZeroError());
- $this->assertEquals('500', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new NotFoundHttpException());
- $this->assertEquals('404', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"'));
- $this->assertEquals('401', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new BadRequestHttpException());
- $this->assertEquals('400', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new NotAcceptableHttpException());
- $this->assertEquals('406', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new ConflictHttpException());
- $this->assertEquals('409', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new MethodNotAllowedHttpException(['POST']));
- $this->assertEquals('405', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new AccessDeniedHttpException());
- $this->assertEquals('403', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new GoneHttpException());
- $this->assertEquals('410', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new LengthRequiredHttpException());
- $this->assertEquals('411', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new PreconditionFailedHttpException());
- $this->assertEquals('412', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new PreconditionRequiredHttpException());
- $this->assertEquals('428', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new ServiceUnavailableHttpException());
- $this->assertEquals('503', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new TooManyRequestsHttpException());
- $this->assertEquals('429', $flattened->getStatusCode());
-
- $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException());
- $this->assertEquals('415', $flattened->getStatusCode());
-
- if (class_exists(SuspiciousOperationException::class)) {
- $flattened = FlattenException::create(new SuspiciousOperationException());
- $this->assertEquals('400', $flattened->getStatusCode());
- }
- }
-
- public function testHeadersForHttpException()
- {
- $flattened = FlattenException::create(new MethodNotAllowedHttpException(['POST']));
- $this->assertEquals(['Allow' => 'POST'], $flattened->getHeaders());
-
- $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"'));
- $this->assertEquals(['WWW-Authenticate' => 'Basic realm="My Realm"'], $flattened->getHeaders());
-
- $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT'));
- $this->assertEquals(['Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'], $flattened->getHeaders());
-
- $flattened = FlattenException::create(new ServiceUnavailableHttpException(120));
- $this->assertEquals(['Retry-After' => 120], $flattened->getHeaders());
-
- $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT'));
- $this->assertEquals(['Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'], $flattened->getHeaders());
-
- $flattened = FlattenException::create(new TooManyRequestsHttpException(120));
- $this->assertEquals(['Retry-After' => 120], $flattened->getHeaders());
- }
-
- /**
- * @dataProvider flattenDataProvider
- */
- public function testFlattenHttpException(\Throwable $exception)
- {
- $flattened = FlattenException::createFromThrowable($exception);
- $flattened2 = FlattenException::createFromThrowable($exception);
-
- $flattened->setPrevious($flattened2);
-
- $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.');
- $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.');
- $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception');
- }
-
- public function testWrappedThrowable()
- {
- $exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42));
- $flattened = FlattenException::create($exception);
-
- $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.');
- $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.');
- $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error');
- }
-
- public function testThrowable()
- {
- $error = new \DivisionByZeroError('Ouch', 42);
- $flattened = FlattenException::createFromThrowable($error);
-
- $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.');
- $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.');
- $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error');
- }
-
- /**
- * @dataProvider flattenDataProvider
- */
- public function testPrevious(\Throwable $exception)
- {
- $flattened = FlattenException::createFromThrowable($exception);
- $flattened2 = FlattenException::createFromThrowable($exception);
-
- $flattened->setPrevious($flattened2);
-
- $this->assertSame($flattened2, $flattened->getPrevious());
-
- $this->assertSame([$flattened2], $flattened->getAllPrevious());
- }
-
- public function testPreviousError()
- {
- $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42));
-
- $flattened = FlattenException::create($exception)->getPrevious();
-
- $this->assertEquals($flattened->getMessage(), 'Oh noes!', 'The message is copied from the original exception.');
- $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.');
- $this->assertEquals($flattened->getClass(), 'ParseError', 'The class is set to the class of the original exception');
- }
-
- /**
- * @dataProvider flattenDataProvider
- */
- public function testLine(\Throwable $exception)
- {
- $flattened = FlattenException::createFromThrowable($exception);
- $this->assertSame($exception->getLine(), $flattened->getLine());
- }
-
- /**
- * @dataProvider flattenDataProvider
- */
- public function testFile(\Throwable $exception)
- {
- $flattened = FlattenException::createFromThrowable($exception);
- $this->assertSame($exception->getFile(), $flattened->getFile());
- }
-
- /**
- * @dataProvider flattenDataProvider
- */
- public function testToArray(\Throwable $exception, string $expectedClass)
- {
- $flattened = FlattenException::createFromThrowable($exception);
- $flattened->setTrace([], 'foo.php', 123);
-
- $this->assertEquals([
- [
- 'message' => 'test',
- 'class' => $expectedClass,
- 'trace' => [[
- 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123,
- 'args' => [],
- ]],
- ],
- ], $flattened->toArray());
- }
-
- public function testCreate()
- {
- $exception = new NotFoundHttpException(
- 'test',
- new \RuntimeException('previous', 123)
- );
-
- $this->assertSame(
- FlattenException::createFromThrowable($exception)->toArray(),
- FlattenException::create($exception)->toArray()
- );
- }
-
- public function flattenDataProvider()
- {
- return [
- [new \Exception('test', 123), 'Exception'],
- [new \Error('test', 123), 'Error'],
- ];
- }
-
- public function testArguments()
- {
- if (\PHP_VERSION_ID >= 70400) {
- $this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
- }
-
- $dh = opendir(__DIR__);
- $fh = tmpfile();
-
- $incomplete = unserialize('O:14:"BogusTestClass":0:{}');
-
- $exception = $this->createException([
- (object) ['foo' => 1],
- new NotFoundHttpException(),
- $incomplete,
- $dh,
- $fh,
- function () {},
- [1, 2],
- ['foo' => 123],
- null,
- true,
- false,
- 0,
- 0.0,
- '0',
- '',
- INF,
- NAN,
- ]);
-
- $flattened = FlattenException::create($exception);
- $trace = $flattened->getTrace();
- $args = $trace[1]['args'];
- $array = $args[0][1];
-
- closedir($dh);
- fclose($fh);
-
- $i = 0;
- $this->assertSame(['object', 'stdClass'], $array[$i++]);
- $this->assertSame(['object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'], $array[$i++]);
- $this->assertSame(['incomplete-object', 'BogusTestClass'], $array[$i++]);
- $this->assertSame(['resource', 'stream'], $array[$i++]);
- $this->assertSame(['resource', 'stream'], $array[$i++]);
-
- $args = $array[$i++];
- $this->assertSame($args[0], 'object');
- $this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.');
-
- $this->assertSame(['array', [['integer', 1], ['integer', 2]]], $array[$i++]);
- $this->assertSame(['array', ['foo' => ['integer', 123]]], $array[$i++]);
- $this->assertSame(['null', null], $array[$i++]);
- $this->assertSame(['boolean', true], $array[$i++]);
- $this->assertSame(['boolean', false], $array[$i++]);
- $this->assertSame(['integer', 0], $array[$i++]);
- $this->assertSame(['float', 0.0], $array[$i++]);
- $this->assertSame(['string', '0'], $array[$i++]);
- $this->assertSame(['string', ''], $array[$i++]);
- $this->assertSame(['float', INF], $array[$i++]);
-
- // assertEquals() does not like NAN values.
- $this->assertEquals($array[$i][0], 'float');
- $this->assertNan($array[$i][1]);
- }
-
- public function testRecursionInArguments()
- {
- if (\PHP_VERSION_ID >= 70400) {
- $this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
- }
-
- $a = null;
- $a = ['foo', [2, &$a]];
- $exception = $this->createException($a);
-
- $flattened = FlattenException::create($exception);
- $trace = $flattened->getTrace();
- $this->assertStringContainsString('*DEEP NESTED ARRAY*', serialize($trace));
- }
-
- public function testTooBigArray()
- {
- if (\PHP_VERSION_ID >= 70400) {
- $this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
- }
-
- $a = [];
- for ($i = 0; $i < 20; ++$i) {
- for ($j = 0; $j < 50; ++$j) {
- for ($k = 0; $k < 10; ++$k) {
- $a[$i][$j][$k] = 'value';
- }
- }
- }
- $a[20] = 'value';
- $a[21] = 'value1';
- $exception = $this->createException($a);
-
- $flattened = FlattenException::create($exception);
- $trace = $flattened->getTrace();
-
- $this->assertSame($trace[1]['args'][0], ['array', ['array', '*SKIPPED over 10000 entries*']]);
-
- $serializeTrace = serialize($trace);
-
- $this->assertStringContainsString('*SKIPPED over 10000 entries*', $serializeTrace);
- $this->assertStringNotContainsString('*value1*', $serializeTrace);
- }
-
- public function testAnonymousClass()
- {
- $flattened = FlattenException::create(new class() extends \RuntimeException {
- });
-
- $this->assertSame('RuntimeException@anonymous', $flattened->getClass());
-
- $flattened = FlattenException::create(new \Exception(sprintf('Class "%s" blah.', \get_class(new class() extends \RuntimeException {
- }))));
-
- $this->assertSame('Class "RuntimeException@anonymous" blah.', $flattened->getMessage());
- }
-
- public function testToStringEmptyMessage()
- {
- $exception = new \RuntimeException();
-
- $flattened = FlattenException::create($exception);
-
- $this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString());
- $this->assertSame($exception->__toString(), $flattened->getAsString());
- }
-
- public function testToString()
- {
- $test = function ($a, $b, $c, $d) {
- return new \RuntimeException('This is a test message');
- };
-
- $exception = $test('foo123', 1, null, 1.5);
-
- $flattened = FlattenException::create($exception);
-
- $this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString());
- $this->assertSame($exception->__toString(), $flattened->getAsString());
- }
-
- public function testToStringParent()
- {
- $exception = new \LogicException('This is message 1');
- $exception = new \RuntimeException('This is messsage 2', 500, $exception);
-
- $flattened = FlattenException::create($exception);
-
- $this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString());
- $this->assertSame($exception->__toString(), $flattened->getAsString());
- }
-
- private function createException($foo)
- {
- return new \Exception();
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php b/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php
deleted file mode 100644
index b3aec0beebe53..0000000000000
--- a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php
+++ /dev/null
@@ -1,178 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\Exception\OutOfMemoryException;
-use Symfony\Component\Debug\ExceptionHandler;
-use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-
-require_once __DIR__.'/HeaderMock.php';
-
-/**
- * @group legacy
- */
-class ExceptionHandlerTest extends TestCase
-{
- protected function setUp(): void
- {
- testHeader();
- }
-
- protected function tearDown(): void
- {
- testHeader();
- }
-
- /**
- * @group legacy
- */
- public function testDebug()
- {
- $handler = new ExceptionHandler(false);
-
- ob_start();
- $handler->sendPhpResponse(new \RuntimeException('Foo'));
- $response = ob_get_clean();
-
- $this->assertStringContainsString('Whoops, looks like something went wrong.', $response);
- $this->assertStringNotContainsString('
', $response);
-
- $handler = new ExceptionHandler(true);
-
- ob_start();
- $handler->sendPhpResponse(new \RuntimeException('Foo'));
- $response = ob_get_clean();
-
- $this->assertStringContainsString('
Foo
', $response);
- $this->assertStringContainsString('
', $response);
-
- // taken from https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
- $htmlWithXss = '
click me!
';
- ob_start();
- $handler->sendPhpResponse(new \RuntimeException($htmlWithXss));
- $response = ob_get_clean();
-
- $this->assertStringContainsString(sprintf('
%s
', htmlspecialchars($htmlWithXss, ENT_COMPAT | ENT_SUBSTITUTE, 'UTF-8')), $response);
- }
-
- public function testStatusCode()
- {
- $handler = new ExceptionHandler(false, 'iso8859-1');
-
- ob_start();
- $handler->sendPhpResponse(new NotFoundHttpException('Foo'));
- $response = ob_get_clean();
-
- $this->assertStringContainsString('Sorry, the page you are looking for could not be found.', $response);
-
- $expectedHeaders = [
- ['HTTP/1.0 404', true, null],
- ['Content-Type: text/html; charset=iso8859-1', true, null],
- ];
-
- $this->assertSame($expectedHeaders, testHeader());
- }
-
- public function testHeaders()
- {
- $handler = new ExceptionHandler(false, 'iso8859-1');
-
- ob_start();
- $handler->sendPhpResponse(new MethodNotAllowedHttpException(['POST']));
- ob_get_clean();
-
- $expectedHeaders = [
- ['HTTP/1.0 405', true, null],
- ['Allow: POST', false, null],
- ['Content-Type: text/html; charset=iso8859-1', true, null],
- ];
-
- $this->assertSame($expectedHeaders, testHeader());
- }
-
- public function testNestedExceptions()
- {
- $handler = new ExceptionHandler(true);
- ob_start();
- $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar')));
- $response = ob_get_clean();
-
- $this->assertStringMatchesFormat('%A
Foo
%A
Bar
%A', $response);
- }
-
- public function testHandle()
- {
- $handler = new ExceptionHandler(true);
- ob_start();
-
- $handler->handle(new \Exception('foo'));
-
- $this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'foo');
- }
-
- public function testHandleWithACustomHandlerThatOutputsSomething()
- {
- $handler = new ExceptionHandler(true);
- ob_start();
- $handler->setHandler(function () {
- echo 'ccc';
- });
-
- $handler->handle(new \Exception());
- ob_end_flush(); // Necessary because of this PHP bug : https://bugs.php.net/76563
- $this->assertSame('ccc', ob_get_clean());
- }
-
- public function testHandleWithACustomHandlerThatOutputsNothing()
- {
- $handler = new ExceptionHandler(true);
- $handler->setHandler(function () {});
-
- $handler->handle(new \Exception('ccc'));
-
- $this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'ccc');
- }
-
- public function testHandleWithACustomHandlerThatFails()
- {
- $handler = new ExceptionHandler(true);
- $handler->setHandler(function () {
- throw new \RuntimeException();
- });
-
- $handler->handle(new \Exception('ccc'));
-
- $this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'ccc');
- }
-
- public function testHandleOutOfMemoryException()
- {
- $handler = new ExceptionHandler(true);
- ob_start();
- $handler->setHandler(function () {
- $this->fail('OutOfMemoryException should bypass the handler');
- });
-
- $handler->handle(new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__));
-
- $this->assertThatTheExceptionWasOutput(ob_get_clean(), OutOfMemoryException::class, 'OutOfMemoryException', 'foo');
- }
-
- private function assertThatTheExceptionWasOutput($content, $expectedClass, $expectedTitle, $expectedMessage)
- {
- $this->assertStringContainsString(sprintf('
%s', $expectedClass, $expectedTitle), $content);
- $this->assertStringContainsString(sprintf('
%s
', $expectedMessage), $content);
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php
deleted file mode 100644
index e71bc98ec6eed..0000000000000
--- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php
+++ /dev/null
@@ -1,187 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
-
-use Composer\Autoload\ClassLoader as ComposerClassLoader;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\DebugClassLoader;
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
-
-/**
- * @group legacy
- */
-class ClassNotFoundFatalErrorHandlerTest extends TestCase
-{
- public static function setUpBeforeClass(): void
- {
- foreach (spl_autoload_functions() as $function) {
- if (!\is_array($function)) {
- continue;
- }
-
- // get class loaders wrapped by DebugClassLoader
- if ($function[0] instanceof DebugClassLoader) {
- $function = $function[0]->getClassLoader();
-
- if (!\is_array($function)) {
- continue;
- }
- }
-
- if ($function[0] instanceof ComposerClassLoader) {
- $function[0]->add('Symfony_Component_Debug_Tests_Fixtures', \dirname(__DIR__, 5));
- break;
- }
- }
- }
-
- /**
- * @dataProvider provideClassNotFoundData
- */
- public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null)
- {
- if ($autoloader) {
- // Unregister all autoloaders to ensure the custom provided
- // autoloader is the only one to be used during the test run.
- $autoloaders = spl_autoload_functions();
- array_map('spl_autoload_unregister', $autoloaders);
- spl_autoload_register($autoloader);
- }
-
- $handler = new ClassNotFoundFatalErrorHandler();
-
- $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
-
- if ($autoloader) {
- spl_autoload_unregister($autoloader);
- array_map('spl_autoload_register', $autoloaders);
- }
-
- $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
- $this->assertSame($translatedMessage, $exception->getMessage());
- $this->assertSame($error['type'], $exception->getSeverity());
- $this->assertSame($error['file'], $exception->getFile());
- $this->assertSame($error['line'], $exception->getLine());
- }
-
- public function provideClassNotFoundData()
- {
- $autoloader = new ComposerClassLoader();
- $autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception'));
- $autoloader->add('Symfony_Component_Debug_Tests_Fixtures', realpath(__DIR__.'/../../Tests/Fixtures'));
-
- $debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']);
-
- return [
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'WhizBangFactory\' not found',
- ],
- "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found',
- ],
- "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'UndefinedFunctionException\' not found',
- ],
- "Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
- [$debugClassLoader, 'loadClass'],
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'PEARClass\' not found',
- ],
- "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?",
- [$debugClassLoader, 'loadClass'],
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
- ],
- "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
- [$debugClassLoader, 'loadClass'],
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
- ],
- "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
- [$autoloader, 'loadClass'],
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
- ],
- "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
- [$debugClassLoader, 'loadClass'],
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
- ],
- "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
- function ($className) { /* do nothing here */ },
- ],
- ];
- }
-
- public function testCannotRedeclareClass()
- {
- if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) {
- $this->markTestSkipped('Can only be run on case insensitive filesystems');
- }
-
- require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP';
-
- $error = [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found',
- ];
-
- $handler = new ClassNotFoundFatalErrorHandler();
- $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
-
- $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php
deleted file mode 100644
index b827c4c67aebb..0000000000000
--- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
-
-/**
- * @group legacy
- */
-class UndefinedFunctionFatalErrorHandlerTest extends TestCase
-{
- /**
- * @dataProvider provideUndefinedFunctionData
- */
- public function testUndefinedFunction($error, $translatedMessage)
- {
- $handler = new UndefinedFunctionFatalErrorHandler();
- $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
-
- $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception);
- // class names are case insensitive and PHP do not return the same
- $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage()));
- $this->assertSame($error['type'], $exception->getSeverity());
- $this->assertSame($error['file'], $exception->getFile());
- $this->assertSame($error['line'], $exception->getLine());
- }
-
- public function provideUndefinedFunctionData()
- {
- return [
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined function test_namespaced_function()',
- ],
- "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()',
- ],
- "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined function foo()',
- ],
- 'Attempted to call function "foo" from the global namespace.',
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()',
- ],
- 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".',
- ],
- ];
- }
-}
-
-function test_namespaced_function()
-{
-}
diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php
deleted file mode 100644
index 7ea1b1f95e786..0000000000000
--- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\Exception\FatalErrorException;
-use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
-
-/**
- * @group legacy
- */
-class UndefinedMethodFatalErrorHandlerTest extends TestCase
-{
- /**
- * @dataProvider provideUndefinedMethodData
- */
- public function testUndefinedMethod($error, $translatedMessage)
- {
- $handler = new UndefinedMethodFatalErrorHandler();
- $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
-
- $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception);
- $this->assertSame($translatedMessage, $exception->getMessage());
- $this->assertSame($error['type'], $exception->getSeverity());
- $this->assertSame($error['file'], $exception->getFile());
- $this->assertSame($error['line'], $exception->getLine());
- }
-
- public function provideUndefinedMethodData()
- {
- return [
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined method SplObjectStorage::what()',
- ],
- 'Attempted to call an undefined method named "what" of class "SplObjectStorage".',
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined method SplObjectStorage::walid()',
- ],
- "Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
- ],
- [
- [
- 'type' => 1,
- 'line' => 12,
- 'file' => 'foo.php',
- 'message' => 'Call to undefined method SplObjectStorage::offsetFet()',
- ],
- "Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
- ],
- [
- [
- 'type' => 1,
- 'message' => 'Call to undefined method class@anonymous::test()',
- 'file' => '/home/possum/work/symfony/test.php',
- 'line' => 11,
- ],
- 'Attempted to call an undefined method named "test" of class "class@anonymous".',
- ],
- ];
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php b/src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php
deleted file mode 100644
index 4eecb6d3f1668..0000000000000
--- a/src/Symfony/Component/Debug/Tests/Fixtures/AnnotatedClass.php
+++ /dev/null
@@ -1,13 +0,0 @@
-exception = $e;
- }
-
- public function __toString()
- {
- try {
- throw $this->exception;
- } catch (\Exception $e) {
- // Using user_error() here is on purpose so we do not forget
- // that this alias also should work alongside with trigger_error().
- return trigger_error($e, E_USER_ERROR);
- }
- }
-}
diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php b/src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php
deleted file mode 100644
index 5eb5fbf84bc93..0000000000000
--- a/src/Symfony/Component/Debug/Tests/Fixtures/TraitWithAnnotatedParameters.php
+++ /dev/null
@@ -1,13 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-function headers_sent()
-{
- return false;
-}
-
-function header($str, $replace = true, $status = null)
-{
- Tests\testHeader($str, $replace, $status);
-}
-
-namespace Symfony\Component\Debug\Tests;
-
-function testHeader()
-{
- static $headers = [];
-
- if (!$h = \func_get_args()) {
- $h = $headers;
- $headers = [];
-
- return $h;
- }
-
- $headers[] = \func_get_args();
-}
diff --git a/src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt b/src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt
deleted file mode 100644
index 041affc14df65..0000000000000
--- a/src/Symfony/Component/Debug/Tests/phpt/debug_class_loader.phpt
+++ /dev/null
@@ -1,28 +0,0 @@
---TEST--
-Test DebugClassLoader with previously loaded parents
---FILE--
-
---EXPECTF--
-%A
-The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".
-The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".
diff --git a/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt b/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt
deleted file mode 100644
index 25167a2c6aaee..0000000000000
--- a/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt
+++ /dev/null
@@ -1,48 +0,0 @@
---TEST--
-Test catching fatal errors when handlers are nested
---INI--
-display_errors=0
---FILE--
-
---EXPECTF--
-%A
-object(Symfony\Component\Debug\Exception\ClassNotFoundException)#%d (8) {
- ["message":protected]=>
- string(131) "Attempted to load class "missing" from namespace "Symfony\Component\Debug".
-Did you forget a "use" statement for another namespace?"
- ["string":"Exception":private]=>
- string(0) ""
- ["code":protected]=>
- int(0)
- ["file":protected]=>
- string(%d) "%s"
- ["line":protected]=>
- int(%d)
- ["trace":"Exception":private]=>
- array(%d) {%A}
- ["previous":"Exception":private]=>
- NULL
- ["severity":protected]=>
- int(1)
-}
diff --git a/src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt b/src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt
deleted file mode 100644
index b743d93ad7c80..0000000000000
--- a/src/Symfony/Component/Debug/Tests/phpt/exception_rethrown.phpt
+++ /dev/null
@@ -1,35 +0,0 @@
---TEST--
-Test rethrowing in custom exception handler
---FILE--
-setDefaultLogger(new TestLogger());
-ini_set('display_errors', 1);
-
-throw new \Exception('foo');
-?>
---EXPECTF--
-Uncaught Exception: foo
-123
-Fatal error: Uncaught %s:25
-Stack trace:
-%a
diff --git a/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt b/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt
deleted file mode 100644
index d0fa2411e6bd9..0000000000000
--- a/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt
+++ /dev/null
@@ -1,43 +0,0 @@
---TEST--
-Test catching fatal errors when handlers are nested
---FILE--
-setExceptionHandler('print_r');
-
-if (true) {
- class Broken implements \JsonSerializable
- {
- }
-}
-
-?>
---EXPECTF--
-array(1) {
- [0]=>
- string(37) "Error and exception handlers do match"
-}
-%A
-object(Symfony\Component\Debug\Exception\FatalErrorException)#%d (%d) {
- ["message":protected]=>
- string(179) "Error: Class Symfony\Component\Debug\Broken contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (JsonSerializable::jsonSerialize)"
-%a
-}
diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json
deleted file mode 100644
index 96fe201bddc8f..0000000000000
--- a/src/Symfony/Component/Debug/composer.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "name": "symfony/debug",
- "type": "library",
- "description": "Symfony Debug Component",
- "keywords": [],
- "homepage": "https://symfony.com",
- "license": "MIT",
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "require": {
- "php": "^7.1.3",
- "psr/log": "~1.0"
- },
- "conflict": {
- "symfony/http-kernel": "<3.4"
- },
- "require-dev": {
- "symfony/http-kernel": "^3.4|^4.0|^5.0"
- },
- "autoload": {
- "psr-4": { "Symfony\\Component\\Debug\\": "" },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "minimum-stability": "dev",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- }
-}
diff --git a/src/Symfony/Component/Debug/phpunit.xml.dist b/src/Symfony/Component/Debug/phpunit.xml.dist
deleted file mode 100644
index a51bbff935861..0000000000000
--- a/src/Symfony/Component/Debug/phpunit.xml.dist
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
- ./Tests/
-
-
- ./Resources/ext/tests/
-
-
-
-
-
- ./
-
- ./Tests
- ./vendor
-
-
-
-
diff --git a/src/Symfony/Component/DependencyInjection/Alias.php b/src/Symfony/Component/DependencyInjection/Alias.php
index 3e74fd92c8308..79e7e243471c8 100644
--- a/src/Symfony/Component/DependencyInjection/Alias.php
+++ b/src/Symfony/Component/DependencyInjection/Alias.php
@@ -44,13 +44,11 @@ public function isPublic()
/**
* Sets if this Alias is public.
*
- * @param bool $boolean If this Alias should be public
- *
* @return $this
*/
- public function setPublic($boolean)
+ public function setPublic(bool $boolean)
{
- $this->public = (bool) $boolean;
+ $this->public = $boolean;
$this->private = false;
return $this;
@@ -64,13 +62,11 @@ public function setPublic($boolean)
* but triggers a deprecation notice when accessed from the container,
* so that the alias can be made really private in 4.0.
*
- * @param bool $boolean
- *
* @return $this
*/
- public function setPrivate($boolean)
+ public function setPrivate(bool $boolean)
{
- $this->private = (bool) $boolean;
+ $this->private = $boolean;
return $this;
}
@@ -96,7 +92,7 @@ public function isPrivate()
*
* @throws InvalidArgumentException when the message template is invalid
*/
- public function setDeprecated($status = true, $template = null)
+ public function setDeprecated(bool $status = true, string $template = null)
{
if (null !== $template) {
if (preg_match('#[\r\n]|\*/#', $template)) {
@@ -110,7 +106,7 @@ public function setDeprecated($status = true, $template = null)
$this->deprecationTemplate = $template;
}
- $this->deprecated = (bool) $status;
+ $this->deprecated = $status;
return $this;
}
diff --git a/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php b/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php
new file mode 100644
index 0000000000000..8eb6d5b4243f2
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Argument;
+
+/**
+ * Represents an abstract service argument, which have to be set by a compiler pass or a DI extension.
+ */
+final class AbstractArgument
+{
+ private $serviceId;
+ private $argKey;
+ private $text;
+
+ public function __construct(string $serviceId, string $argKey, string $text = '')
+ {
+ $this->serviceId = $serviceId;
+ $this->argKey = $argKey;
+ $this->text = $text;
+ }
+
+ public function getServiceId(): string
+ {
+ return $this->serviceId;
+ }
+
+ public function getArgumentKey(): string
+ {
+ return $this->argKey;
+ }
+
+ public function getText(): string
+ {
+ return $this->text;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md
index b514ce1c8367a..0bacf3d561da7 100644
--- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md
+++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md
@@ -1,6 +1,25 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * added support to autowire public typed properties in php 7.4
+ * added support for defining method calls, a configurator, and property setters in `InlineServiceConfigurator`
+ * added possibility to define abstract service arguments
+
+5.0.0
+-----
+
+ * removed support for auto-discovered extension configuration class which does not implement `ConfigurationInterface`
+ * removed support for non-string default env() parameters
+ * moved `ServiceSubscriberInterface` to the `Symfony\Contracts\Service` namespace
+ * removed `RepeatedPass` and `RepeatablePassInterface`
+ * removed support for short factory/configurator syntax from `YamlFileLoader`
+ * removed `ResettableContainerInterface`, use `ResetInterface` instead
+ * added argument `$returnsClone` to `Definition::addMethodCall()`
+ * removed `tagged`, use `tagged_iterator` instead
+
4.4.0
-----
diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php
index 7718f725b18bd..657a7fa826ea0 100644
--- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php
+++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php
@@ -109,7 +109,7 @@ public function replaceArgument($index, $value)
/**
* @internal
*/
- public function setAutoconfigured($autoconfigured): self
+ public function setAutoconfigured(bool $autoconfigured): self
{
throw new BadMethodCallException('A ChildDefinition cannot be autoconfigured.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
index 02a18b6cf09ca..0d00d52dca3f1 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -68,11 +68,10 @@ protected function inExpression(bool $reset = true): bool
* Processes a value found in a definition tree.
*
* @param mixed $value
- * @param bool $isRoot
*
* @return mixed The processed value
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (\is_array($value)) {
foreach ($value as $k => $v) {
@@ -105,13 +104,11 @@ protected function processValue($value, $isRoot = false)
}
/**
- * @param bool $required
- *
* @return \ReflectionFunctionAbstract|null
*
* @throws RuntimeException
*/
- protected function getConstructor(Definition $definition, $required)
+ protected function getConstructor(Definition $definition, bool $required)
{
if ($definition->isSynthetic()) {
return null;
@@ -167,13 +164,11 @@ protected function getConstructor(Definition $definition, $required)
}
/**
- * @param string $method
- *
* @throws RuntimeException
*
* @return \ReflectionFunctionAbstract
*/
- protected function getReflectionMethod(Definition $definition, $method)
+ protected function getReflectionMethod(Definition $definition, string $method)
{
if ('__construct' === $method) {
return $this->getConstructor($definition, true);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
index 59a27aef83b71..beb488236c069 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
@@ -27,7 +27,7 @@
* @author Johannes M. Schmitt
* @author Nicolas Grekas
*/
-class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface
+class AnalyzeServiceReferencesPass extends AbstractRecursivePass
{
private $graph;
private $currentDefinition;
@@ -48,14 +48,6 @@ public function __construct(bool $onlyConstructorArguments = false, bool $hasPro
$this->enableExpressionProcessing();
}
- /**
- * {@inheritdoc}
- */
- public function setRepeatedPass(RepeatedPass $repeatedPass)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- }
-
/**
* Processes a ContainerBuilder object to populate the service reference graph.
*/
@@ -81,7 +73,7 @@ public function process(ContainerBuilder $container)
}
}
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
$lazy = $this->lazy;
$inExpression = $this->inExpression();
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
index 8d46fd6311035..ba10e92b1e7c3 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
@@ -66,7 +66,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
try {
return $this->doProcessValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php
index 20d31135a73ce..c46d71f2068a0 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php
@@ -23,7 +23,7 @@ class AutowireRequiredMethodsPass extends AbstractRecursivePass
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
$value = parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php
new file mode 100644
index 0000000000000..934bde8dd1fe8
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\TypedReference;
+
+/**
+ * Looks for definitions with autowiring enabled and registers their corresponding "@required" properties.
+ *
+ * @author Sebastien Morel (Plopix)
+ * @author Nicolas Grekas
+ */
+class AutowireRequiredPropertiesPass extends AbstractRecursivePass
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function processValue($value, bool $isRoot = false)
+ {
+ if (\PHP_VERSION_ID < 70400) {
+ return $value;
+ }
+ $value = parent::processValue($value, $isRoot);
+
+ if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
+ return $value;
+ }
+ if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
+ return $value;
+ }
+
+ $properties = $value->getProperties();
+ foreach ($reflectionClass->getProperties() as $reflectionProperty) {
+ if (false === $doc = $reflectionProperty->getDocComment()) {
+ continue;
+ }
+ if (false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
+ continue;
+ }
+ if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) {
+ continue;
+ }
+
+ $type = $reflectionProperty->getType()->getName();
+ $value->setProperty($name, new TypedReference($type, $type, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name));
+ }
+
+ return $value;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php
index d289c05b15c09..348498db269cf 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckArgumentsValidityPass.php
@@ -32,7 +32,7 @@ public function __construct(bool $throwExceptions = true)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Definition) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
index eef71587cdd46..4ffe3540cee7a 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
@@ -43,7 +43,7 @@ public function process(ContainerBuilder $container)
}
}
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Reference) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
index 8f2a3bdf706cf..0349ef7616b8a 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
@@ -25,7 +25,7 @@
*/
class CheckReferenceValidityPass extends AbstractRecursivePass
{
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($isRoot && $value instanceof Definition && ($value->isSynthetic() || $value->isAbstract())) {
return $value;
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php
index 14dedf007b092..04ae8d51cb9df 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php
@@ -53,11 +53,8 @@ public function getServiceReferenceGraph()
/**
* Adds a pass to the PassConfig.
- *
- * @param string $type The type of the pass
- * @param int $priority Used to sort the passes
*/
- public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
+ public function addPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
{
$this->passConfig->addPass($pass, $type, $priority);
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php
index 5ee0ff1f491cc..5e7ba3173e5b4 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php
@@ -26,7 +26,7 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Definition || !$value->hasErrors()) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
index ac3b4fe352f2b..41af126947349 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
@@ -22,10 +22,9 @@
*
* @author Johannes M. Schmitt
*/
-class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface
+class InlineServiceDefinitionsPass extends AbstractRecursivePass
{
private $analyzingPass;
- private $repeatedPass;
private $cloningIds = [];
private $connectedIds = [];
private $notInlinedIds = [];
@@ -37,15 +36,6 @@ public function __construct(AnalyzeServiceReferencesPass $analyzingPass = null)
$this->analyzingPass = $analyzingPass;
}
- /**
- * {@inheritdoc}
- */
- public function setRepeatedPass(RepeatedPass $repeatedPass)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- $this->repeatedPass = $repeatedPass;
- }
-
public function process(ContainerBuilder $container)
{
$this->container = $container;
@@ -94,10 +84,6 @@ public function process(ContainerBuilder $container)
}
} while ($this->inlinedIds && $this->analyzingPass);
- if ($this->inlinedIds && $this->repeatedPass) {
- $this->repeatedPass->setRepeat();
- }
-
foreach ($remainingInlinedIds as $id) {
$definition = $container->getDefinition($id);
@@ -115,7 +101,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof ArgumentInterface) {
// Reference found in ArgumentInterface::getValues() are not inlineable
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php
index 264dd8046c819..bfeb7b84b9baf 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php
@@ -167,7 +167,7 @@ public function __construct(ExtensionInterface $extension, ParameterBagInterface
/**
* {@inheritdoc}
*/
- public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0): self
+ public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0): self
{
throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', \get_class($pass), $this->extensionClass));
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
index 1683957fb96a4..b6d475c770ff6 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
@@ -49,6 +49,7 @@ public function __construct()
];
$this->optimizationPasses = [[
+ new AutoAliasServicePass(),
new ValidateEnvPlaceholdersPass(),
new ResolveChildDefinitionsPass(),
new RegisterServiceSubscribersPass(),
@@ -56,6 +57,7 @@ public function __construct()
new ResolveFactoryClassPass(),
new ResolveNamedArgumentsPass(),
new AutowireRequiredMethodsPass(),
+ new AutowireRequiredPropertiesPass(),
new ResolveBindingsPass(),
new ServiceLocatorTagPass(),
new DecoratorServicePass(),
@@ -113,12 +115,9 @@ public function getPasses()
/**
* Adds a pass.
*
- * @param string $type The pass type
- * @param int $priority Used to sort the passes
- *
* @throws InvalidArgumentException when a pass type doesn't exist
*/
- public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
+ public function addPass(CompilerPassInterface $pass, string $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
{
$property = $type.'Passes';
if (!isset($this->$property)) {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
index 14bf000863c9d..7d9c366da8895 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
@@ -28,7 +28,7 @@
*/
class RegisterServiceSubscribersPass extends AbstractRecursivePass
{
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Definition || $value->isAbstract() || $value->isSynthetic() || !$value->hasTag('container.service_subscriber')) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php
index 287af23b77f02..cf1a3ddc7a7a8 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php
@@ -20,18 +20,10 @@
* @author Johannes M. Schmitt
* @author Nicolas Grekas
*/
-class RemoveUnusedDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface
+class RemoveUnusedDefinitionsPass extends AbstractRecursivePass
{
private $connectedIds = [];
- /**
- * {@inheritdoc}
- */
- public function setRepeatedPass(RepeatedPass $repeatedPass)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- }
-
/**
* Processes the ContainerBuilder to remove unused definitions.
*/
@@ -83,7 +75,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Reference) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php
deleted file mode 100644
index 11a5b0d54ffa9..0000000000000
--- a/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection\Compiler;
-
-/**
- * Interface that must be implemented by passes that are run as part of an
- * RepeatedPass.
- *
- * @author Johannes M. Schmitt
- *
- * @deprecated since Symfony 4.2.
- */
-interface RepeatablePassInterface extends CompilerPassInterface
-{
- public function setRepeatedPass(RepeatedPass $repeatedPass);
-}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php
deleted file mode 100644
index b8add1b83d263..0000000000000
--- a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php
+++ /dev/null
@@ -1,83 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection\Compiler;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2.', RepeatedPass::class), E_USER_DEPRECATED);
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
-
-/**
- * A pass that might be run repeatedly.
- *
- * @author Johannes M. Schmitt
- *
- * @deprecated since Symfony 4.2.
- */
-class RepeatedPass implements CompilerPassInterface
-{
- /**
- * @var bool
- */
- private $repeat = false;
-
- private $passes;
-
- /**
- * @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects
- *
- * @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface
- */
- public function __construct(array $passes)
- {
- foreach ($passes as $pass) {
- if (!$pass instanceof RepeatablePassInterface) {
- throw new InvalidArgumentException('$passes must be an array of RepeatablePassInterface.');
- }
-
- $pass->setRepeatedPass($this);
- }
-
- $this->passes = $passes;
- }
-
- /**
- * Process the repeatable passes that run more than once.
- */
- public function process(ContainerBuilder $container)
- {
- do {
- $this->repeat = false;
- foreach ($this->passes as $pass) {
- $pass->process($container);
- }
- } while ($this->repeat);
- }
-
- /**
- * Sets if the pass should repeat.
- */
- public function setRepeat()
- {
- $this->repeat = true;
- }
-
- /**
- * Returns the passes.
- *
- * @return RepeatablePassInterface[] An array of RepeatablePassInterface objects
- */
- public function getPasses()
- {
- return $this->passes;
- }
-}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
index 99810963612d1..ca781f2801a93 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
@@ -80,7 +80,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof Reference && isset($this->replacements[$referenceId = (string) $value])) {
// Perform the replacement
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
index 4df81b1183b4c..761aa314faa05 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
@@ -92,7 +92,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof TypedReference && $value->getType() === (string) $value) {
// Already checked
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php
index 453e3f63c3f2a..f180d2290b4ae 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php
@@ -29,7 +29,7 @@ class ResolveChildDefinitionsPass extends AbstractRecursivePass
{
private $currentPath;
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Definition) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php
index 9e1edd4d31568..ea52b1459247f 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php
@@ -18,7 +18,7 @@
*/
class ResolveEnvPlaceholdersPass extends AbstractRecursivePass
{
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (\is_string($value)) {
return $this->container->resolveEnvPlaceholders($value, true);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveFactoryClassPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveFactoryClassPass.php
index 848da7f2bd24b..23f535b71dbec 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveFactoryClassPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveFactoryClassPass.php
@@ -22,7 +22,7 @@ class ResolveFactoryClassPass extends AbstractRecursivePass
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof Definition && \is_array($factory = $value->getFactory()) && null === $factory[0]) {
if (null === $class = $value->getClass()) {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveHotPathPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveHotPathPass.php
index bfdd91c417f97..0d01e2a6f7f0b 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveHotPathPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveHotPathPass.php
@@ -47,7 +47,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof ArgumentInterface) {
return $value;
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
index 6004cc3151489..8fa3868c5a1b8 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php
@@ -26,7 +26,7 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Definition) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
index 32eb6a3a76f3e..b1c81ee3a1414 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
@@ -60,7 +60,7 @@ public function process(ContainerBuilder $container)
$this->bag = null;
}
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (\is_string($value)) {
try {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php
index bf9ea2b21acc7..086bc8780aeed 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php
@@ -42,7 +42,7 @@ public function process(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof Reference) {
return parent::processValue($value, $isRoot);
@@ -62,7 +62,7 @@ private function getDefinitionId(string $id, ContainerBuilder $container): strin
$alias = $container->getAlias($id);
if ($alias->isDeprecated()) {
- @trigger_error(sprintf('%s. It is being referenced by the "%s" %s.', rtrim($alias->getDeprecationMessage($id), '. '), $this->currentId, $container->hasDefinition($this->currentId) ? 'service' : 'alias'), E_USER_DEPRECATED);
+ trigger_deprecation('', '', '%s. It is being referenced by the "%s" %s.', rtrim($alias->getDeprecationMessage($id), '. '), $this->currentId, $container->hasDefinition($this->currentId) ? 'service' : 'alias');
}
$seen = [];
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveServiceSubscribersPass.php
index 399f349046a36..518c03d7e7ba7 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveServiceSubscribersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveServiceSubscribersPass.php
@@ -25,7 +25,7 @@ class ResolveServiceSubscribersPass extends AbstractRecursivePass
{
private $serviceLocator;
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof Reference && $this->serviceLocator && \in_array((string) $value, [ContainerInterface::class, ServiceProviderInterface::class], true)) {
return new Reference($this->serviceLocator);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php
index a4305722f7cb5..48a034a847e00 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveTaggedIteratorArgumentPass.php
@@ -25,7 +25,7 @@ class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass
/**
* {@inheritdoc}
*/
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if (!$value instanceof TaggedIteratorArgument) {
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php
index 5fdbe5686dbfa..5985451b34b63 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php
@@ -29,7 +29,7 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
{
use PriorityTaggedServiceTrait;
- protected function processValue($value, $isRoot = false)
+ protected function processValue($value, bool $isRoot = false)
{
if ($value instanceof ServiceLocatorArgument) {
if ($value->getTaggedIteratorArgument()) {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php
index 8fd84fac00eeb..be0f1edd201fe 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\Config\Definition\BaseNode;
-use Symfony\Component\Config\Definition\Exception\TreeWithoutRootNodeException;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
@@ -80,10 +79,7 @@ public function process(ContainerBuilder $container)
continue;
}
- try {
- $this->extensionConfig[$name] = $processor->processConfiguration($configuration, $config);
- } catch (TreeWithoutRootNodeException $e) {
- }
+ $this->extensionConfig[$name] = $processor->processConfiguration($configuration, $config);
}
} finally {
BaseNode::resetPlaceholders();
diff --git a/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResource.php b/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResource.php
index e9a4fffe2ad1c..8ffb8dcac9f94 100644
--- a/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResource.php
+++ b/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResource.php
@@ -18,7 +18,7 @@
*
* @author Maxime Steinhausser
*
- * @final since Symfony 4.3
+ * @final
*/
class ContainerParametersResource implements ResourceInterface
{
@@ -35,7 +35,7 @@ public function __construct(array $parameters)
/**
* {@inheritdoc}
*/
- public function __toString()
+ public function __toString(): string
{
return 'container_parameters_'.md5(serialize($this->parameters));
}
@@ -43,7 +43,7 @@ public function __toString()
/**
* @return array Tracked parameters
*/
- public function getParameters()
+ public function getParameters(): array
{
return $this->parameters;
}
diff --git a/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResourceChecker.php b/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResourceChecker.php
index 6ed77e3fd2c48..2f2affaa5b6c2 100644
--- a/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResourceChecker.php
+++ b/src/Symfony/Component/DependencyInjection/Config/ContainerParametersResourceChecker.php
@@ -39,7 +39,7 @@ public function supports(ResourceInterface $metadata)
/**
* {@inheritdoc}
*/
- public function isFresh(ResourceInterface $resource, $timestamp)
+ public function isFresh(ResourceInterface $resource, int $timestamp)
{
foreach ($resource->getParameters() as $key => $value) {
if (!$this->container->hasParameter($key) || $this->container->getParameter($key) !== $value) {
diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php
index 00bacd2ac6ded..53bebdfcc58da 100644
--- a/src/Symfony/Component/DependencyInjection/Container.php
+++ b/src/Symfony/Component/DependencyInjection/Container.php
@@ -39,7 +39,7 @@
* @author Fabien Potencier
* @author Johannes M. Schmitt
*/
-class Container implements ResettableContainerInterface
+class Container implements ContainerInterface, ResetInterface
{
protected $parameterBag;
protected $services = [];
@@ -107,7 +107,7 @@ public function getParameterBag()
*
* @throws InvalidArgumentException if the parameter is not defined
*/
- public function getParameter($name)
+ public function getParameter(string $name)
{
return $this->parameterBag->get($name);
}
@@ -119,7 +119,7 @@ public function getParameter($name)
*
* @return bool The presence of parameter in container
*/
- public function hasParameter($name)
+ public function hasParameter(string $name)
{
return $this->parameterBag->has($name);
}
@@ -130,7 +130,7 @@ public function hasParameter($name)
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
- public function setParameter($name, $value)
+ public function setParameter(string $name, $value)
{
$this->parameterBag->set($name, $value);
}
@@ -140,11 +140,8 @@ public function setParameter($name, $value)
*
* Setting a synthetic service to null resets it: has() returns false and get()
* behaves in the same way as if the service was never created.
- *
- * @param string $id The service identifier
- * @param object|null $service The service instance
*/
- public function set($id, $service)
+ public function set(string $id, ?object $service)
{
// Runs the internal initializer; used by the dumped container to include always-needed files
if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) {
@@ -218,17 +215,11 @@ public function has($id)
*
* @see Reference
*/
- public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1)
+ public function get($id, int $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1)
{
- $service = $this->services[$id]
+ return $this->services[$id]
?? $this->services[$id = $this->aliases[$id] ?? $id]
?? ('service_container' === $id ? $this : ($this->factories[$id] ?? [$this, 'make'])($id, $invalidBehavior));
-
- if (!\is_object($service) && null !== $service) {
- @trigger_error(sprintf('Non-object services are deprecated since Symfony 4.4, please fix the "%s" service which is of type "%s" right now.', $id, \gettype($service)), E_USER_DEPRECATED);
- }
-
- return $service;
}
/**
@@ -293,7 +284,7 @@ private function make(string $id, int $invalidBehavior)
*
* @return bool true if service has already been initialized, false otherwise
*/
- public function initialized($id)
+ public function initialized(string $id)
{
if (isset($this->aliases[$id])) {
$id = $this->aliases[$id];
diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
index 247a021fd786e..ad89d4e87112e 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
@@ -20,6 +20,7 @@
use Symfony\Component\Config\Resource\GlobResource;
use Symfony\Component\Config\Resource\ReflectionClassResource;
use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -160,12 +161,10 @@ public function __construct(ParameterBagInterface $parameterBag = null)
*
* If you are not using the loaders and therefore don't want
* to depend on the Config component, set this flag to false.
- *
- * @param bool $track True if you want to track resources, false otherwise
*/
- public function setResourceTracking($track)
+ public function setResourceTracking(bool $track)
{
- $this->trackResources = (bool) $track;
+ $this->trackResources = $track;
}
/**
@@ -198,13 +197,11 @@ public function registerExtension(ExtensionInterface $extension)
/**
* Returns an extension by alias or namespace.
*
- * @param string $name An alias or a namespace
- *
* @return ExtensionInterface An extension instance
*
* @throws LogicException if the extension is not registered
*/
- public function getExtension($name)
+ public function getExtension(string $name)
{
if (isset($this->extensions[$name])) {
return $this->extensions[$name];
@@ -230,11 +227,9 @@ public function getExtensions()
/**
* Checks if we have an extension.
*
- * @param string $name The name of the extension
- *
* @return bool If the extension exists
*/
- public function hasExtension($name)
+ public function hasExtension(string $name)
{
return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
}
@@ -423,7 +418,7 @@ public function fileExists(string $path, $trackContents = true): bool
* @throws BadMethodCallException When this ContainerBuilder is compiled
* @throws \LogicException if the extension is not registered
*/
- public function loadFromExtension($extension, array $values = null)
+ public function loadFromExtension(string $extension, array $values = null)
{
if ($this->isCompiled()) {
throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
@@ -448,7 +443,7 @@ public function loadFromExtension($extension, array $values = null)
*
* @return $this
*/
- public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
+ public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0)
{
$this->getCompiler()->addPass($pass, $type, $priority);
@@ -484,19 +479,10 @@ public function getCompiler()
/**
* Sets a service.
*
- * @param string $id The service identifier
- * @param object|null $service The service instance
- *
* @throws BadMethodCallException When this ContainerBuilder is compiled
*/
- public function set($id, $service)
+ public function set(string $id, ?object $service)
{
- if (!\is_object($service) && null !== $service) {
- @trigger_error(sprintf('Non-object services are deprecated since Symfony 4.4, setting the "%s" service to a value of type "%s" should be avoided.', $id, \gettype($service)), E_USER_DEPRECATED);
- }
-
- $id = (string) $id;
-
if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
// setting a synthetic service on a compiled container is alright
throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
@@ -509,12 +495,10 @@ public function set($id, $service)
/**
* Removes a service definition.
- *
- * @param string $id The service identifier
*/
- public function removeDefinition($id)
+ public function removeDefinition(string $id)
{
- if (isset($this->definitions[$id = (string) $id])) {
+ if (isset($this->definitions[$id])) {
unset($this->definitions[$id]);
$this->removedIds[$id] = true;
}
@@ -549,19 +533,13 @@ public function has($id)
*
* @see Reference
*/
- public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
+ public function get($id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
{
if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior) {
return parent::get($id);
}
- $service = $this->doGet($id, $invalidBehavior);
-
- if (!\is_object($service) && null !== $service) {
- @trigger_error(sprintf('Non-object services are deprecated since Symfony 4.4, please fix the "%s" service which is of type "%s" right now.', $id, \gettype($service)), E_USER_DEPRECATED);
- }
-
- return $service;
+ return $this->doGet($id, $invalidBehavior);
}
private function doGet(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, bool $isConstructorArgument = false)
@@ -590,7 +568,7 @@ private function doGet(string $id, int $invalidBehavior = ContainerInterface::EX
$alias = $this->aliasDefinitions[$id];
if ($alias->isDeprecated()) {
- @trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED);
+ trigger_deprecation('', '', $alias->getDeprecationMessage($id));
}
return $this->doGet((string) $alias, $invalidBehavior, $inlineServices, $isConstructorArgument);
@@ -697,11 +675,9 @@ public function merge(self $container)
/**
* Returns the configuration array for the given extension.
*
- * @param string $name The name of the extension
- *
* @return array An array of configuration
*/
- public function getExtensionConfig($name)
+ public function getExtensionConfig(string $name)
{
if (!isset($this->extensionConfigs[$name])) {
$this->extensionConfigs[$name] = [];
@@ -712,11 +688,8 @@ public function getExtensionConfig($name)
/**
* Prepends a config array to the configs of the given extension.
- *
- * @param string $name The name of the extension
- * @param array $config The config to set
*/
- public function prependExtensionConfig($name, array $config)
+ public function prependExtensionConfig(string $name, array $config)
{
if (!isset($this->extensionConfigs[$name])) {
$this->extensionConfigs[$name] = [];
@@ -834,10 +807,8 @@ public function setAliases(array $aliases)
* @throws InvalidArgumentException if the id is not a string or an Alias
* @throws InvalidArgumentException if the alias is for itself
*/
- public function setAlias($alias, $id)
+ public function setAlias(string $alias, $id)
{
- $alias = (string) $alias;
-
if ('' === $alias || '\\' === $alias[-1] || \strlen($alias) !== strcspn($alias, "\0\r\n'")) {
throw new InvalidArgumentException(sprintf('Invalid alias id: "%s"', $alias));
}
@@ -862,9 +833,9 @@ public function setAlias($alias, $id)
*
* @param string $alias The alias to remove
*/
- public function removeAlias($alias)
+ public function removeAlias(string $alias)
{
- if (isset($this->aliasDefinitions[$alias = (string) $alias])) {
+ if (isset($this->aliasDefinitions[$alias])) {
unset($this->aliasDefinitions[$alias]);
$this->removedIds[$alias] = true;
}
@@ -873,13 +844,11 @@ public function removeAlias($alias)
/**
* Returns true if an alias exists under the given identifier.
*
- * @param string $id The service identifier
- *
* @return bool true if the alias exists, false otherwise
*/
- public function hasAlias($id)
+ public function hasAlias(string $id)
{
- return isset($this->aliasDefinitions[$id = (string) $id]);
+ return isset($this->aliasDefinitions[$id]);
}
/**
@@ -895,16 +864,12 @@ public function getAliases()
/**
* Gets an alias.
*
- * @param string $id The service identifier
- *
* @return Alias An Alias instance
*
* @throws InvalidArgumentException if the alias does not exist
*/
- public function getAlias($id)
+ public function getAlias(string $id)
{
- $id = (string) $id;
-
if (!isset($this->aliasDefinitions[$id])) {
throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
}
@@ -918,12 +883,9 @@ public function getAlias($id)
* This methods allows for simple registration of service definition
* with a fluid interface.
*
- * @param string $id The service identifier
- * @param string|null $class The service class
- *
* @return Definition A Definition instance
*/
- public function register($id, $class = null)
+ public function register(string $id, string $class = null)
{
return $this->setDefinition($id, new Definition($class));
}
@@ -934,12 +896,9 @@ public function register($id, $class = null)
* This method implements a shortcut for using setDefinition() with
* an autowired definition.
*
- * @param string $id The service identifier
- * @param string|null $class The service class
- *
* @return Definition The created definition
*/
- public function autowire($id, $class = null)
+ public function autowire(string $id, string $class = null)
{
return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
}
@@ -980,20 +939,16 @@ public function getDefinitions()
/**
* Sets a service definition.
*
- * @param string $id The service identifier
- *
* @return Definition the service definition
*
* @throws BadMethodCallException When this ContainerBuilder is compiled
*/
- public function setDefinition($id, Definition $definition)
+ public function setDefinition(string $id, Definition $definition)
{
if ($this->isCompiled()) {
throw new BadMethodCallException('Adding definition to a compiled container is not allowed');
}
- $id = (string) $id;
-
if ('' === $id || '\\' === $id[-1] || \strlen($id) !== strcspn($id, "\0\r\n'")) {
throw new InvalidArgumentException(sprintf('Invalid service id: "%s"', $id));
}
@@ -1006,28 +961,22 @@ public function setDefinition($id, Definition $definition)
/**
* Returns true if a service definition exists under the given identifier.
*
- * @param string $id The service identifier
- *
* @return bool true if the service definition exists, false otherwise
*/
- public function hasDefinition($id)
+ public function hasDefinition(string $id)
{
- return isset($this->definitions[(string) $id]);
+ return isset($this->definitions[$id]);
}
/**
* Gets a service definition.
*
- * @param string $id The service identifier
- *
* @return Definition A Definition instance
*
* @throws ServiceNotFoundException if the service definition does not exist
*/
- public function getDefinition($id)
+ public function getDefinition(string $id)
{
- $id = (string) $id;
-
if (!isset($this->definitions[$id])) {
throw new ServiceNotFoundException($id);
}
@@ -1040,16 +989,12 @@ public function getDefinition($id)
*
* The method "unaliases" recursively to return a Definition instance.
*
- * @param string $id The service identifier or alias
- *
* @return Definition A Definition instance
*
* @throws ServiceNotFoundException if the service definition does not exist
*/
- public function findDefinition($id)
+ public function findDefinition(string $id)
{
- $id = (string) $id;
-
$seen = [];
while (isset($this->aliasDefinitions[$id])) {
$id = (string) $this->aliasDefinitions[$id];
@@ -1092,7 +1037,7 @@ private function createService(Definition $definition, array &$inlineServices, b
}
if ($definition->isDeprecated()) {
- @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED);
+ trigger_deprecation('', '', $definition->getDeprecationMessage($id));
}
if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) {
@@ -1135,7 +1080,7 @@ private function createService(Definition $definition, array &$inlineServices, b
$r = new \ReflectionClass($factory[0]);
if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
- @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name);
}
}
} else {
@@ -1144,7 +1089,7 @@ private function createService(Definition $definition, array &$inlineServices, b
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
- @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name);
}
}
@@ -1271,6 +1216,8 @@ private function doResolveServices($value, array &$inlineServices = [], bool $is
$value = $this->getParameter((string) $value);
} elseif ($value instanceof Expression) {
$value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]);
+ } elseif ($value instanceof AbstractArgument) {
+ throw new RuntimeException(sprintf('Argument "%s" of service "%s" is abstract%s, did you forget to define it?', $value->getArgumentKey(), $value->getServiceId(), $value->getText() ? ' ('.$value->getText().')' : ''));
}
return $value;
@@ -1290,12 +1237,9 @@ private function doResolveServices($value, array &$inlineServices = [], bool $is
* }
* }
*
- * @param string $name
- * @param bool $throwOnAbstract
- *
* @return array An array of tags with the tagged service as key, holding a list of attribute arrays
*/
- public function findTaggedServiceIds($name, $throwOnAbstract = false)
+ public function findTaggedServiceIds(string $name, bool $throwOnAbstract = false)
{
$this->usedTags[] = $name;
$tags = [];
@@ -1352,11 +1296,9 @@ public function getExpressionLanguageProviders()
/**
* Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
*
- * @param string $interface The class or interface to match
- *
* @return ChildDefinition
*/
- public function registerForAutoconfiguration($interface)
+ public function registerForAutoconfiguration(string $interface)
{
if (!isset($this->autoconfiguredInstanceof[$interface])) {
$this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
diff --git a/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
index 0a7f018315f0f..616648f84cb96 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerInterface.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
@@ -32,11 +32,8 @@ interface ContainerInterface extends PsrContainerInterface
/**
* Sets a service.
- *
- * @param string $id The service identifier
- * @param object|null $service The service instance
*/
- public function set($id, $service);
+ public function set(string $id, ?object $service);
/**
* Gets a service.
@@ -51,7 +48,7 @@ public function set($id, $service);
*
* @see Reference
*/
- public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE);
+ public function get($id, int $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE);
/**
* Returns true if the given service is defined.
@@ -65,11 +62,9 @@ public function has($id);
/**
* Check for whether or not a service has been initialized.
*
- * @param string $id
- *
* @return bool true if the service has been initialized, false otherwise
*/
- public function initialized($id);
+ public function initialized(string $id);
/**
* Gets a parameter.
@@ -80,7 +75,7 @@ public function initialized($id);
*
* @throws InvalidArgumentException if the parameter is not defined
*/
- public function getParameter($name);
+ public function getParameter(string $name);
/**
* Checks if a parameter exists.
@@ -89,7 +84,7 @@ public function getParameter($name);
*
* @return bool The presence of parameter in container
*/
- public function hasParameter($name);
+ public function hasParameter(string $name);
/**
* Sets a parameter.
@@ -97,5 +92,5 @@ public function hasParameter($name);
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
- public function setParameter($name, $value);
+ public function setParameter(string $name, $value);
}
diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php
index e2bc713a3a911..83b7cd2d7f852 100644
--- a/src/Symfony/Component/DependencyInjection/Definition.php
+++ b/src/Symfony/Component/DependencyInjection/Definition.php
@@ -63,11 +63,7 @@ class Definition
*/
public $decorationOnInvalid;
- /**
- * @param string|null $class The service class
- * @param array $arguments An array of arguments to pass to the service constructor
- */
- public function __construct($class = null, array $arguments = [])
+ public function __construct(string $class = null, array $arguments = [])
{
if (null !== $class) {
$this->setClass($class);
@@ -134,23 +130,19 @@ public function getFactory()
/**
* Sets the service that this service is decorating.
*
- * @param string|null $id The decorated service id, use null to remove decoration
- * @param string|null $renamedId The new decorated service id
- * @param int $priority The priority of decoration
- * @param int $invalidBehavior The behavior to adopt when decorated is invalid
+ * @param string|null $id The decorated service id, use null to remove decoration
+ * @param string|null $renamedId The new decorated service id
*
* @return $this
*
* @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals
*/
- public function setDecoratedService($id, $renamedId = null, $priority = 0/*, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE*/)
+ public function setDecoratedService(?string $id, ?string $renamedId = null, int $priority = 0, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
{
if ($renamedId && $id === $renamedId) {
throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
}
- $invalidBehavior = 3 < \func_num_args() ? (int) func_get_arg(3) : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
-
$this->changes['decorated_service'] = true;
if (null === $id) {
@@ -179,19 +171,10 @@ public function getDecoratedService()
/**
* Sets the service class.
*
- * @param string $class The service class
- *
* @return $this
*/
- public function setClass($class)
+ public function setClass(?string $class)
{
- if ($class instanceof Parameter) {
- @trigger_error(sprintf('Passing an instance of %s as class name to %s in deprecated in Symfony 4.4 and will result in a TypeError in 5.0. Please pass the string "%%%s%%" instead.', Parameter::class, __CLASS__, (string) $class), E_USER_DEPRECATED);
- }
- if (null !== $class && !\is_string($class)) {
- @trigger_error(sprintf('The class name passed to %s is expected to be a string. Passing a %s is deprecated in Symfony 4.4 and will result in a TypeError in 5.0.', __CLASS__, \is_object($class) ? \get_class($class) : \gettype($class)), E_USER_DEPRECATED);
- }
-
$this->changes['class'] = true;
$this->class = $class;
@@ -246,12 +229,11 @@ public function getProperties()
/**
* Sets a specific property.
*
- * @param string $name
- * @param mixed $value
+ * @param mixed $value
*
* @return $this
*/
- public function setProperty($name, $value)
+ public function setProperty(string $name, $value)
{
$this->properties[$name] = $value;
@@ -370,12 +352,12 @@ public function setMethodCalls(array $calls = [])
*
* @throws InvalidArgumentException on empty $method param
*/
- public function addMethodCall($method, array $arguments = []/*, bool $returnsClone = false*/)
+ public function addMethodCall(string $method, array $arguments = [], bool $returnsClone = false)
{
if (empty($method)) {
throw new InvalidArgumentException('Method name cannot be empty.');
}
- $this->calls[] = 2 < \func_num_args() && func_get_arg(2) ? [$method, $arguments, true] : [$method, $arguments];
+ $this->calls[] = $returnsClone ? [$method, $arguments, true] : [$method, $arguments];
return $this;
}
@@ -383,11 +365,9 @@ public function addMethodCall($method, array $arguments = []/*, bool $returnsClo
/**
* Removes a method to call after service initialization.
*
- * @param string $method The method name to remove
- *
* @return $this
*/
- public function removeMethodCall($method)
+ public function removeMethodCall(string $method)
{
foreach ($this->calls as $i => $call) {
if ($call[0] === $method) {
@@ -402,11 +382,9 @@ public function removeMethodCall($method)
/**
* Check if the current definition has a given method to call after service initialization.
*
- * @param string $method The method name to search for
- *
* @return bool
*/
- public function hasMethodCall($method)
+ public function hasMethodCall(string $method)
{
foreach ($this->calls as $call) {
if ($call[0] === $method) {
@@ -454,11 +432,9 @@ public function getInstanceofConditionals()
/**
* Sets whether or not instanceof conditionals should be prepended with a global set.
*
- * @param bool $autoconfigured
- *
* @return $this
*/
- public function setAutoconfigured($autoconfigured)
+ public function setAutoconfigured(bool $autoconfigured)
{
$this->changes['autoconfigured'] = true;
@@ -500,11 +476,9 @@ public function getTags()
/**
* Gets a tag by name.
*
- * @param string $name The tag name
- *
* @return array An array of attributes
*/
- public function getTag($name)
+ public function getTag(string $name)
{
return isset($this->tags[$name]) ? $this->tags[$name] : [];
}
@@ -512,12 +486,9 @@ public function getTag($name)
/**
* Adds a tag for this definition.
*
- * @param string $name The tag name
- * @param array $attributes An array of attributes
- *
* @return $this
*/
- public function addTag($name, array $attributes = [])
+ public function addTag(string $name, array $attributes = [])
{
$this->tags[$name][] = $attributes;
@@ -527,11 +498,9 @@ public function addTag($name, array $attributes = [])
/**
* Whether this definition has a tag with the given name.
*
- * @param string $name
- *
* @return bool
*/
- public function hasTag($name)
+ public function hasTag(string $name)
{
return isset($this->tags[$name]);
}
@@ -539,11 +508,9 @@ public function hasTag($name)
/**
* Clears all tags for a given name.
*
- * @param string $name The tag name
- *
* @return $this
*/
- public function clearTag($name)
+ public function clearTag(string $name)
{
unset($this->tags[$name]);
@@ -565,11 +532,9 @@ public function clearTags()
/**
* Sets a file to require before creating the service.
*
- * @param string $file A full pathname to include
- *
* @return $this
*/
- public function setFile($file)
+ public function setFile(?string $file)
{
$this->changes['file'] = true;
@@ -591,15 +556,13 @@ public function getFile()
/**
* Sets if the service must be shared or not.
*
- * @param bool $shared Whether the service must be shared or not
- *
* @return $this
*/
- public function setShared($shared)
+ public function setShared(bool $shared)
{
$this->changes['shared'] = true;
- $this->shared = (bool) $shared;
+ $this->shared = $shared;
return $this;
}
@@ -617,15 +580,13 @@ public function isShared()
/**
* Sets the visibility of this service.
*
- * @param bool $boolean
- *
* @return $this
*/
- public function setPublic($boolean)
+ public function setPublic(bool $boolean)
{
$this->changes['public'] = true;
- $this->public = (bool) $boolean;
+ $this->public = $boolean;
$this->private = false;
return $this;
@@ -649,13 +610,11 @@ public function isPublic()
* but triggers a deprecation notice when accessed from the container,
* so that the service can be made really private in 4.0.
*
- * @param bool $boolean
- *
* @return $this
*/
- public function setPrivate($boolean)
+ public function setPrivate(bool $boolean)
{
- $this->private = (bool) $boolean;
+ $this->private = $boolean;
return $this;
}
@@ -673,15 +632,13 @@ public function isPrivate()
/**
* Sets the lazy flag of this service.
*
- * @param bool $lazy
- *
* @return $this
*/
- public function setLazy($lazy)
+ public function setLazy(bool $lazy)
{
$this->changes['lazy'] = true;
- $this->lazy = (bool) $lazy;
+ $this->lazy = $lazy;
return $this;
}
@@ -700,13 +657,11 @@ public function isLazy()
* Sets whether this definition is synthetic, that is not constructed by the
* container, but dynamically injected.
*
- * @param bool $boolean
- *
* @return $this
*/
- public function setSynthetic($boolean)
+ public function setSynthetic(bool $boolean)
{
- $this->synthetic = (bool) $boolean;
+ $this->synthetic = $boolean;
return $this;
}
@@ -726,13 +681,11 @@ public function isSynthetic()
* Whether this definition is abstract, that means it merely serves as a
* template for other definitions.
*
- * @param bool $boolean
- *
* @return $this
*/
- public function setAbstract($boolean)
+ public function setAbstract(bool $boolean)
{
- $this->abstract = (bool) $boolean;
+ $this->abstract = $boolean;
return $this;
}
@@ -752,14 +705,13 @@ public function isAbstract()
* Whether this definition is deprecated, that means it should not be called
* anymore.
*
- * @param bool $status
* @param string $template Template message to use if the definition is deprecated
*
* @return $this
*
* @throws InvalidArgumentException when the message template is invalid
*/
- public function setDeprecated($status = true, $template = null)
+ public function setDeprecated(bool $status = true, string $template = null)
{
if (null !== $template) {
if (preg_match('#[\r\n]|\*/#', $template)) {
@@ -775,7 +727,7 @@ public function setDeprecated($status = true, $template = null)
$this->changes['deprecated'] = true;
- $this->deprecated = (bool) $status;
+ $this->deprecated = $status;
return $this;
}
@@ -798,7 +750,7 @@ public function isDeprecated()
*
* @return string
*/
- public function getDeprecationMessage($id)
+ public function getDeprecationMessage(string $id)
{
return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate);
}
@@ -848,15 +800,13 @@ public function isAutowired()
/**
* Enables/disables autowiring.
*
- * @param bool $autowired
- *
* @return $this
*/
- public function setAutowired($autowired)
+ public function setAutowired(bool $autowired)
{
$this->changes['autowired'] = true;
- $this->autowired = (bool) $autowired;
+ $this->autowired = $autowired;
return $this;
}
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 15d5c09f767ca..9f9ebbaa219b3 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -13,6 +13,7 @@
use Composer\Autoload\ClassLoader;
use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -20,6 +21,7 @@
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
+use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphEdge;
use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphNode;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -150,8 +152,8 @@ public function dump(array $options = [])
$this->namespace = $options['namespace'];
$this->asFiles = $options['as_files'];
$this->hotPathTag = $options['hot_path_tag'];
- $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']);
- $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']);
+ $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && (!$this->container->hasParameter($options['inline_factories_parameter']) || $this->container->getParameter($options['inline_factories_parameter']));
+ $this->inlineRequires = $options['inline_class_loader_parameter'] && ($this->container->hasParameter($options['inline_class_loader_parameter']) ? $this->container->getParameter($options['inline_class_loader_parameter']) : (\PHP_VERSION_ID < 70400 || $options['debug']));
$this->serviceLocatorTag = $options['service_locator_tag'];
if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) {
@@ -393,6 +395,9 @@ private function getProxyDumper(): ProxyDumper
return $this->proxyDumper;
}
+ /**
+ * @param ServiceReferenceGraphEdge[] $edges
+ */
private function analyzeCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$currentPath = [], bool $byConstructor = true)
{
$checkedNodes[$sourceId] = true;
@@ -798,7 +803,7 @@ protected function {$methodName}($lazyInitialization)
$this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls);
if ($definition->isDeprecated()) {
- $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
+ $code .= sprintf(" trigger_deprecation('', '', %s);\n\n", $this->export($definition->getDeprecationMessage($id)));
}
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
@@ -1307,7 +1312,7 @@ private function addDeprecatedAliases(): string
*/
protected function {$methodNameAlias}()
{
- @trigger_error($messageExported, E_USER_DEPRECATED);
+ trigger_deprecation('', '', $messageExported);
return \$this->get($idExported);
}
@@ -1381,14 +1386,13 @@ private function addDefaultParametersMethod(): string
$code = <<<'EOF'
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return $this->buildParameters[$name];
}
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -1398,17 +1402,16 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return true;
}
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
@@ -1431,7 +1434,7 @@ public function getParameterBag(): ParameterBagInterface
EOF;
if (!$this->asFiles) {
- $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code);
+ $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code);
}
if ($dynamicPhp) {
@@ -1737,6 +1740,8 @@ private function dumpValue($value, bool $interpolate = true): string
return $code;
}
+ } elseif ($value instanceof AbstractArgument) {
+ throw new RuntimeException(sprintf('Argument "%s" of service "%s" is abstract%s, did you forget to define it?', $value->getArgumentKey(), $value->getServiceId(), $value->getText() ? ' ('.$value->getText().')' : ''));
} elseif (\is_object($value) || \is_resource($value)) {
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
index fb5d827acdc0a..6f7d918d26af4 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Dumper;
use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
@@ -312,6 +313,14 @@ private function convertParameters(array $parameters, string $type, \DOMElement
$element->setAttribute('type', 'binary');
$text = $this->document->createTextNode(self::phpToXml(base64_encode($value)));
$element->appendChild($text);
+ } elseif ($value instanceof AbstractArgument) {
+ $argKey = $value->getArgumentKey();
+ if (!is_numeric($argKey)) {
+ $element->setAttribute('key', $argKey);
+ }
+ $element->setAttribute('type', 'abstract');
+ $text = $this->document->createTextNode(self::phpToXml($value->getText()));
+ $element->appendChild($text);
} else {
if (\in_array($value, ['null', 'true', 'false'], true)) {
$element->setAttribute('type', 'string');
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
index ccb68ee8f1ee1..bba092c64e90d 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Dumper;
use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -284,6 +285,8 @@ private function dumpValue($value)
return $this->getExpressionCall((string) $value);
} elseif ($value instanceof Definition) {
return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']);
+ } elseif ($value instanceof AbstractArgument) {
+ return new TaggedValue('abstract', $value->getText());
} elseif (\is_object($value) || \is_resource($value)) {
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
}
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
index b7c828b124eb3..987605f98f25c 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
@@ -61,7 +61,7 @@ public static function getProvidedTypes()
/**
* {@inheritdoc}
*/
- public function getEnv($prefix, $name, \Closure $getEnv)
+ public function getEnv(string $prefix, string $name, \Closure $getEnv)
{
$i = strpos($name, ':');
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessorInterface.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessorInterface.php
index 654fe55e982fc..d3275fe290e36 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessorInterface.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessorInterface.php
@@ -31,7 +31,7 @@ interface EnvVarProcessorInterface
*
* @throws RuntimeException on error
*/
- public function getEnv($prefix, $name, \Closure $getEnv);
+ public function getEnv(string $prefix, string $name, \Closure $getEnv);
/**
* @return string[] The PHP-types managed by getEnv(), keyed by prefixes
diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
index d2093761c76c7..81070c911fab9 100644
--- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php
+++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
@@ -17,6 +17,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\LogicException;
/**
* Provides useful features shared by many extensions.
@@ -93,10 +94,7 @@ public function getConfiguration(array $config, ContainerBuilder $container)
}
if (!$class->implementsInterface(ConfigurationInterface::class)) {
- @trigger_error(sprintf('Not implementing "%s" in the extension configuration class "%s" is deprecated since Symfony 4.1.', ConfigurationInterface::class, $class->getName()), E_USER_DEPRECATED);
- //throw new LogicException(sprintf('The extension configuration class "%s" must implement "%s".', $class->getName(), ConfigurationInterface::class));
-
- return null;
+ throw new LogicException(sprintf('The extension configuration class "%s" must implement "%s".', $class->getName(), ConfigurationInterface::class));
}
if (!($constructor = $class->getConstructor()) || !$constructor->getNumberOfRequiredParameters()) {
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php
index 96104e4ad66af..a9d78115dd83c 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php
@@ -30,5 +30,5 @@ interface InstantiatorInterface
*
* @return object
*/
- public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator);
+ public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator);
}
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php
index 2e36704080e6d..1696e7a90482f 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php
@@ -26,7 +26,7 @@ class RealServiceInstantiator implements InstantiatorInterface
/**
* {@inheritdoc}
*/
- public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator)
+ public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator)
{
return $realInstantiator();
}
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php
index f592e6cf196c2..351560d2926ad 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php
@@ -30,12 +30,9 @@ public function isProxyCandidate(Definition $definition);
/**
* Generates the code to be used to instantiate a proxy in the dumped factory code.
*
- * @param string $id Service identifier
- * @param string $factoryCode The code to execute to create the service
- *
* @return string
*/
- public function getProxyFactoryCode(Definition $definition, $id, $factoryCode);
+ public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode);
/**
* Generates the code for the lazy proxy.
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php
index ebc6a5dc2bb5f..7e0f14c32bb1b 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php
@@ -33,7 +33,7 @@ public function isProxyCandidate(Definition $definition): bool
/**
* {@inheritdoc}
*/
- public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = null): string
+ public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode): string
{
return '';
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php b/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php
index 939dd7cb7c4a0..57aa83d6a0a74 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php
@@ -33,7 +33,7 @@ public function __construct(ContainerBuilder $container)
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
$resource($this->container);
}
@@ -41,7 +41,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
return $resource instanceof \Closure;
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php
index a12fd1bed5758..d5554a76755cb 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php
@@ -22,10 +22,15 @@ abstract class AbstractConfigurator
{
const FACTORY = 'unknown';
+ /**
+ * @var callable(mixed $value, bool $allowService)|null
+ */
+ public static $valuePreProcessor;
+
/** @internal */
protected $definition;
- public function __call($method, $args)
+ public function __call(string $method, array $args)
{
if (method_exists($this, 'set'.$method)) {
return $this->{'set'.$method}(...$args);
@@ -49,7 +54,11 @@ public static function processValue($value, $allowServices = false)
$value[$k] = static::processValue($v, $allowServices);
}
- return $value;
+ return self::$valuePreProcessor ? (self::$valuePreProcessor)($value, $allowServices) : $value;
+ }
+
+ if (self::$valuePreProcessor) {
+ $value = (self::$valuePreProcessor)($value, $allowServices);
}
if ($value instanceof ReferenceConfigurator) {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php
index 712d8c96439b6..f511f82f2a1ae 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php
@@ -69,6 +69,17 @@ final public function services(): ServicesConfigurator
{
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount);
}
+
+ /**
+ * @return static
+ */
+ final public function withPath(string $path): self
+ {
+ $clone = clone $this;
+ $clone->path = $clone->file = $path;
+
+ return $clone;
+ }
}
/**
@@ -107,18 +118,6 @@ function iterator(array $values): IteratorArgument
return new IteratorArgument(AbstractConfigurator::processValue($values, true));
}
-/**
- * Creates a lazy iterator by tag name.
- *
- * @deprecated since Symfony 4.4, to be removed in 5.0, use "tagged_iterator" instead.
- */
-function tagged(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null): TaggedIteratorArgument
-{
- @trigger_error(__NAMESPACE__.'\tagged() is deprecated since Symfony 4.4 and will be removed in 5.0, use '.__NAMESPACE__.'\tagged_iterator() instead.', E_USER_DEPRECATED);
-
- return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod);
-}
-
/**
* Creates a lazy iterator by tag name.
*/
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
index 362b374e55970..9802de884a205 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
@@ -23,10 +23,13 @@ class InlineServiceConfigurator extends AbstractConfigurator
use Traits\ArgumentTrait;
use Traits\AutowireTrait;
use Traits\BindTrait;
+ use Traits\CallTrait;
+ use Traits\ConfiguratorTrait;
use Traits\FactoryTrait;
use Traits\FileTrait;
use Traits\LazyTrait;
use Traits\ParentTrait;
+ use Traits\PropertyTrait;
use Traits\TagTrait;
public function __construct(Definition $definition)
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/DecorateTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/DecorateTrait.php
index 222cf758132e7..b3a1ae1b54793 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/DecorateTrait.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/DecorateTrait.php
@@ -19,10 +19,7 @@ trait DecorateTrait
/**
* Sets the service that this service is decorating.
*
- * @param string|null $id The decorated service id, use null to remove decoration
- * @param string|null $renamedId The new decorated service id
- * @param int $priority The priority of decoration
- * @param int $invalidBehavior The behavior to adopt when decorated is invalid
+ * @param string|null $id The decorated service id, use null to remove decoration
*
* @return $this
*
diff --git a/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
index a57cac3b5ee99..943986982e344 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
@@ -21,7 +21,7 @@ class DirectoryLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function load($file, $type = null)
+ public function load($file, string $type = null)
{
$file = rtrim($file, '/');
$path = $this->locator->locate($file);
@@ -43,7 +43,7 @@ public function load($file, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
if ('directory' === $type) {
return true;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
index b96cd416fd413..09290f9b68d52 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
@@ -48,18 +48,16 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l
/**
* {@inheritdoc}
*
- * @param bool|string $ignoreErrors Whether errors should be ignored; pass "not_found" to ignore only when the loaded resource is not found
- * @param string|string[]|null $exclude Glob patterns to exclude from the import
+ * @param bool|string $ignoreErrors Whether errors should be ignored; pass "not_found" to ignore only when the loaded resource is not found
*/
- public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null/*, $exclude = null*/)
+ public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null, $exclude = null)
{
$args = \func_get_args();
if ($ignoreNotFound = 'not_found' === $ignoreErrors) {
$args[2] = false;
} elseif (!\is_bool($ignoreErrors)) {
- @trigger_error(sprintf('Invalid argument $ignoreErrors provided to %s::import(): boolean or "not_found" expected, %s given.', static::class, \gettype($ignoreErrors)), E_USER_DEPRECATED);
- $args[2] = (bool) $ignoreErrors;
+ throw new \TypeError(sprintf('Invalid argument $ignoreErrors provided to %s::import(): boolean or "not_found" expected, %s given.', static::class, \gettype($ignoreErrors)));
}
try {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/GlobFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/GlobFileLoader.php
index 4b25610efe48e..53af9cf2b8a18 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/GlobFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/GlobFileLoader.php
@@ -21,7 +21,7 @@ class GlobFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
foreach ($this->glob($resource, false, $globResource) as $path => $info) {
$this->import($path);
@@ -33,7 +33,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
return 'glob' === $type;
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php
index 40137b18fd7a3..e6384803c36ea 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php
@@ -24,7 +24,7 @@ class IniFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
$path = $this->locator->locate($resource);
@@ -49,7 +49,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
if (!\is_string($resource)) {
return false;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
index f1477ecfd712d..207fb83ec180f 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
@@ -58,7 +58,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
if (!\is_string($resource)) {
return false;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index 41a9f0a3ac933..6cad9453048e7 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -13,6 +13,7 @@
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
@@ -41,7 +42,7 @@ class XmlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
$path = $this->locator->locate($resource);
@@ -75,7 +76,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
if (!\is_string($resource)) {
return false;
@@ -537,6 +538,10 @@ private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file
}
$arguments[$key] = $value;
break;
+ case 'abstract':
+ $serviceId = $node->getAttribute('id');
+ $arguments[$key] = new AbstractArgument($serviceId, (string) $key, $arg->nodeValue);
+ break;
case 'string':
$arguments[$key] = $arg->nodeValue;
break;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index ee8c1401513c0..1722f7ac73479 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
@@ -115,7 +116,7 @@ class YamlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
$path = $this->locator->locate($resource);
@@ -160,7 +161,7 @@ public function load($resource, $type = null)
/**
* {@inheritdoc}
*/
- public function supports($resource, $type = null)
+ public function supports($resource, string $type = null)
{
if (!\is_string($resource)) {
return false;
@@ -446,7 +447,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa
}
if (isset($service['arguments'])) {
- $definition->setArguments($this->resolveServices($service['arguments'], $file));
+ $definition->setArguments($this->resolveServices($service['arguments'], $file, false, $id));
}
if (isset($service['properties'])) {
@@ -634,14 +635,6 @@ private function parseCallable($callable, string $parameter, string $id, string
throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s" in "%s").', $parameter, $id, $callable, substr($callable, 1), $file));
}
- if (false !== strpos($callable, ':') && false === strpos($callable, '::')) {
- $parts = explode(':', $callable);
-
- @trigger_error(sprintf('Using short %s syntax for service "%s" is deprecated since Symfony 4.4, use "[\'@%s\', \'%s\']" instead.', $parameter, $id, ...$parts), E_USER_DEPRECATED);
-
- return [$this->resolveServices('@'.$parts[0], $file), $parts[1]];
- }
-
return $callable;
}
@@ -730,7 +723,7 @@ private function validate($content, string $file): ?array
*
* @return array|string|Reference|ArgumentInterface
*/
- private function resolveServices($value, string $file, bool $isParameter = false)
+ private function resolveServices($value, string $file, bool $isParameter = false, string $serviceId = '', string $argKey = '')
{
if ($value instanceof TaggedValue) {
$argument = $value->getValue();
@@ -803,13 +796,16 @@ private function resolveServices($value, string $file, bool $isParameter = false
return new Reference($id);
}
+ if ('abstract' === $value->getTag()) {
+ return new AbstractArgument($serviceId, $argKey, $value->getValue());
+ }
throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag()));
}
if (\is_array($value)) {
foreach ($value as $k => $v) {
- $value[$k] = $this->resolveServices($v, $file, $isParameter);
+ $value[$k] = $this->resolveServices($v, $file, $isParameter, $serviceId, $k);
}
} elseif (\is_string($value) && 0 === strpos($value, '@=')) {
if (!class_exists(Expression::class)) {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
index 2f745c3326d49..d2c81bcf311c7 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
+++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
@@ -260,6 +260,7 @@
+
diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php
index 872408690b926..defedbbd28f53 100644
--- a/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php
+++ b/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php
@@ -29,7 +29,7 @@ class EnvPlaceholderParameterBag extends ParameterBag
/**
* {@inheritdoc}
*/
- public function get($name)
+ public function get(string $name)
{
if (0 === strpos($name, 'env(') && ')' === substr($name, -1) && 'env()' !== $name) {
$env = substr($name, 4, -1);
@@ -44,23 +44,15 @@ public function get($name)
return $placeholder; // return first result
}
}
- if (!preg_match('/^(?:\w*+:)*+\w++$/', $env)) {
+ if (!preg_match('/^(?:[-.\w]*+:)*+\w++$/', $env)) {
throw new InvalidArgumentException(sprintf('Invalid %s name: only "word" characters are allowed.', $name));
}
-
- if ($this->has($name)) {
- $defaultValue = parent::get($name);
-
- if (null !== $defaultValue && !is_scalar($defaultValue)) { // !is_string in 5.0
- //throw new RuntimeException(sprintf('The default value of an env() parameter must be a string or null, but "%s" given to "%s".', \gettype($defaultValue), $name));
- throw new RuntimeException(sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', \gettype($defaultValue), $name));
- } elseif (is_scalar($defaultValue) && !\is_string($defaultValue)) {
- @trigger_error(sprintf('A non-string default value of an env() parameter is deprecated since 4.3, cast "%s" to string instead.', $name), E_USER_DEPRECATED);
- }
+ if ($this->has($name) && null !== ($defaultValue = parent::get($name)) && !\is_string($defaultValue)) {
+ throw new RuntimeException(sprintf('The default value of an env() parameter must be a string or null, but "%s" given to "%s".', \gettype($defaultValue), $name));
}
$uniqueName = md5($name.'_'.self::$counter++);
- $placeholder = sprintf('%s_%s_%s', $this->getEnvPlaceholderUniquePrefix(), str_replace(':', '_', $env), $uniqueName);
+ $placeholder = sprintf('%s_%s_%s', $this->getEnvPlaceholderUniquePrefix(), strtr($env, ':-.', '___'), $uniqueName);
$this->envPlaceholders[$env][$placeholder] = $placeholder;
return $placeholder;
@@ -154,19 +146,8 @@ public function resolve()
parent::resolve();
foreach ($this->envPlaceholders as $env => $placeholders) {
- if (!$this->has($name = "env($env)")) {
- continue;
- }
- if (is_numeric($default = $this->parameters[$name])) {
- if (!\is_string($default)) {
- @trigger_error(sprintf('A non-string default value of env parameter "%s" is deprecated since 4.3, cast it to string instead.', $env), E_USER_DEPRECATED);
- }
- $this->parameters[$name] = (string) $default;
- } elseif (null !== $default && !is_scalar($default)) { // !is_string in 5.0
- //throw new RuntimeException(sprintf('The default value of env parameter "%s" must be a string or null, %s given.', $env, \gettype($default)));
- throw new RuntimeException(sprintf('The default value of env parameter "%s" must be scalar or null, %s given.', $env, \gettype($default)));
- } elseif (is_scalar($default) && !\is_string($default)) {
- @trigger_error(sprintf('A non-string default value of env parameter "%s" is deprecated since 4.3, cast it to string instead.', $env), E_USER_DEPRECATED);
+ if ($this->has($name = "env($env)") && null !== ($default = $this->parameters[$name]) && !\is_string($default)) {
+ throw new RuntimeException(sprintf('The default value of env parameter "%s" must be a string or null, %s given.', $env, \gettype($default)));
}
}
}
diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php
index a5199937e233f..5a4aaf8b2a43d 100644
--- a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php
+++ b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php
@@ -53,7 +53,7 @@ public function add(array $parameters)
/**
* {@inheritdoc}
*/
- public function set($name, $value)
+ public function set(string $name, $value)
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
@@ -61,7 +61,7 @@ public function set($name, $value)
/**
* {@inheritdoc}
*/
- public function remove($name)
+ public function remove(string $name)
{
throw new LogicException('Impossible to call remove() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
index 48887c3756d26..643424ad55043 100644
--- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
+++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php
@@ -64,10 +64,8 @@ public function all()
/**
* {@inheritdoc}
*/
- public function get($name)
+ public function get(string $name)
{
- $name = (string) $name;
-
if (!\array_key_exists($name, $this->parameters)) {
if (!$name) {
throw new ParameterNotFoundException($name);
@@ -109,15 +107,15 @@ public function get($name)
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
- public function set($name, $value)
+ public function set(string $name, $value)
{
- $this->parameters[(string) $name] = $value;
+ $this->parameters[$name] = $value;
}
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
return \array_key_exists((string) $name, $this->parameters);
}
@@ -127,9 +125,9 @@ public function has($name)
*
* @param string $name The parameter name
*/
- public function remove($name)
+ public function remove(string $name)
{
- unset($this->parameters[(string) $name]);
+ unset($this->parameters[$name]);
}
/**
@@ -190,8 +188,7 @@ public function resolveValue($value, array $resolving = [])
/**
* Resolves parameters inside a string.
*
- * @param string $value The string to resolve
- * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
+ * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
*
* @return mixed The resolved string
*
@@ -199,7 +196,7 @@ public function resolveValue($value, array $resolving = [])
* @throws ParameterCircularReferenceException if a circular reference if detected
* @throws RuntimeException when a given parameter has a type problem
*/
- public function resolveString($value, array $resolving = [])
+ public function resolveString(string $value, array $resolving = [])
{
// we do this to deal with non string values (Boolean, integer, ...)
// as the preg_replace_callback throw an exception when trying
diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php
index 6a4e0fa4acc0a..f224216cc3d83 100644
--- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php
+++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php
@@ -47,39 +47,32 @@ public function all();
/**
* Gets a service container parameter.
*
- * @param string $name The parameter name
- *
* @return mixed The parameter value
*
* @throws ParameterNotFoundException if the parameter is not defined
*/
- public function get($name);
+ public function get(string $name);
/**
* Removes a parameter.
- *
- * @param string $name The parameter name
*/
- public function remove($name);
+ public function remove(string $name);
/**
* Sets a service container parameter.
*
- * @param string $name The parameter name
- * @param mixed $value The parameter value
+ * @param mixed $value The parameter value
*
* @throws LogicException if the parameter can not be set
*/
- public function set($name, $value);
+ public function set(string $name, $value);
/**
* Returns true if a parameter name is defined.
*
- * @param string $name The parameter name
- *
* @return bool true if the parameter name is defined, false otherwise
*/
- public function has($name);
+ public function has(string $name);
/**
* Replaces parameter placeholders (%name%) by their values for all parameters.
diff --git a/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php b/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php
deleted file mode 100644
index b9714d25098fe..0000000000000
--- a/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-use Symfony\Contracts\Service\ResetInterface;
-
-/**
- * ResettableContainerInterface defines additional resetting functionality
- * for containers, allowing to release shared services when the container is
- * not needed anymore.
- *
- * @author Christophe Coevoet
- *
- * @deprecated since Symfony 4.2, use "Symfony\Contracts\Service\ResetInterface" instead.
- */
-interface ResettableContainerInterface extends ContainerInterface, ResetInterface
-{
- /**
- * Resets shared services from the container.
- *
- * The container is not intended to be used again after being reset in a normal workflow. This method is
- * meant as a way to release references for ref-counting.
- * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service.
- */
- public function reset();
-}
diff --git a/src/Symfony/Component/DependencyInjection/ReverseContainer.php b/src/Symfony/Component/DependencyInjection/ReverseContainer.php
index 076e624c2e5ce..280e9e2dd5a63 100644
--- a/src/Symfony/Component/DependencyInjection/ReverseContainer.php
+++ b/src/Symfony/Component/DependencyInjection/ReverseContainer.php
@@ -31,7 +31,7 @@ public function __construct(Container $serviceContainer, ContainerInterface $rev
$this->serviceContainer = $serviceContainer;
$this->reversibleLocator = $reversibleLocator;
$this->tagName = $tagName;
- $this->getServiceId = \Closure::bind(function ($service): ?string {
+ $this->getServiceId = \Closure::bind(function (object $service): ?string {
return array_search($service, $this->services, true) ?: array_search($service, $this->privates, true) ?: null;
}, $serviceContainer, Container::class);
}
@@ -40,10 +40,8 @@ public function __construct(Container $serviceContainer, ContainerInterface $rev
* Returns the id of the passed object when it exists as a service.
*
* To be reversible, services need to be either public or be tagged with "container.reversible".
- *
- * @param object $service
*/
- public function getId($service): ?string
+ public function getId(object $service): ?string
{
if ($this->serviceContainer === $service) {
return 'service_container';
@@ -61,11 +59,9 @@ public function getId($service): ?string
}
/**
- * @return object
- *
* @throws ServiceNotFoundException When the service is not reversible
*/
- public function getService(string $id)
+ public function getService(string $id): object
{
if ($this->serviceContainer->has($id)) {
return $this->serviceContainer->get($id);
diff --git a/src/Symfony/Component/DependencyInjection/ServiceLocator.php b/src/Symfony/Component/DependencyInjection/ServiceLocator.php
index e379f3b607cbb..dd97e39d138d3 100644
--- a/src/Symfony/Component/DependencyInjection/ServiceLocator.php
+++ b/src/Symfony/Component/DependencyInjection/ServiceLocator.php
@@ -57,7 +57,7 @@ public function get($id)
}
}
- public function __invoke($id)
+ public function __invoke(string $id)
{
return isset($this->factories[$id]) ? $this->get($id) : null;
}
@@ -67,7 +67,7 @@ public function __invoke($id)
*
* @return static
*/
- public function withContext(string $externalId, Container $container)
+ public function withContext(string $externalId, Container $container): self
{
$locator = clone $this;
$locator->externalId = $externalId;
diff --git a/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php b/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php
deleted file mode 100644
index a3b6ba790718e..0000000000000
--- a/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php
+++ /dev/null
@@ -1,23 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-use Symfony\Contracts\Service\ServiceSubscriberInterface as BaseServiceSubscriberInterface;
-
-/**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.2, use Symfony\Contracts\Service\ServiceSubscriberInterface instead.
- */
-interface ServiceSubscriberInterface extends BaseServiceSubscriberInterface
-{
-}
diff --git a/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php b/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php
index 90b297fff2f37..2e32cd5977a38 100644
--- a/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php
+++ b/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php
@@ -25,5 +25,5 @@ interface TaggedContainerInterface extends ContainerInterface
*
* @return array An array of tags
*/
- public function findTaggedServiceIds($name);
+ public function findTaggedServiceIds(string $name);
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php b/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php
new file mode 100644
index 0000000000000..91b1a5665b7de
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Tests\Argument;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
+
+class AbstractArgumentTest extends TestCase
+{
+ public function testAbstractArgumentGetters()
+ {
+ $argument = new AbstractArgument('foo', '$bar', 'should be defined by Pass');
+ $this->assertSame('foo', $argument->getServiceId());
+ $this->assertSame('$bar', $argument->getArgumentKey());
+ $this->assertSame('should be defined by Pass', $argument->getText());
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php
new file mode 100644
index 0000000000000..241daaaff3358
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Tests\Compiler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredPropertiesPass;
+use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
+
+if (\PHP_VERSION_ID >= 70400) {
+ require_once __DIR__.'/../Fixtures/includes/autowiring_classes_74.php';
+}
+
+/**
+ * @requires PHP 7.4
+ */
+class AutowireRequiredPropertiesPassTest extends TestCase
+{
+ public function testInjection()
+ {
+ $container = new ContainerBuilder();
+ $container->register(Bar::class);
+ $container->register(A::class);
+ $container->register(B::class);
+ $container->register(PropertiesInjection::class)->setAutowired(true);
+
+ (new ResolveClassPass())->process($container);
+ (new AutowireRequiredPropertiesPass())->process($container);
+
+ $properties = $container->getDefinition(PropertiesInjection::class)->getProperties();
+
+ $this->assertArrayHasKey('plop', $properties);
+ $this->assertEquals(Bar::class, (string) $properties['plop']);
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php
index 3402bbd0c5f96..ed111d6d2c0de 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php
@@ -244,7 +244,7 @@ public function testProcessLeavesServiceLocatorTagOnOriginalDefinition()
protected function process(ContainerBuilder $container)
{
- $repeatedPass = new DecoratorServicePass();
- $repeatedPass->process($container);
+ $pass = new DecoratorServicePass();
+ $pass->process($container);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
index 26e50f484b0e1..f6c255484adc6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
@@ -20,12 +20,12 @@
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
-use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedForDefaultPriorityClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooTagClass;
use Symfony\Contracts\Service\ServiceProviderInterface;
+use Symfony\Contracts\Service\ServiceSubscriberInterface;
/**
* This class tests the integration of the different compiler passes.
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php
index 4686f483a7d61..902fe4eaa9730 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php
@@ -73,7 +73,7 @@ public function testBadProcessor()
class SimpleProcessor implements EnvVarProcessorInterface
{
- public function getEnv($prefix, $name, \Closure $getEnv)
+ public function getEnv(string $prefix, string $name, \Closure $getEnv)
{
return $getEnv($name);
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
index 77b1969f60b1c..6046f79639e99 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
@@ -22,7 +22,6 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
-use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition2;
@@ -31,6 +30,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberChild;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberParent;
use Symfony\Component\DependencyInjection\TypedReference;
+use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;
require_once __DIR__.'/../Fixtures/includes/classes.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
index 3a89b11c8c8ae..f44890c6e662e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
@@ -14,7 +14,6 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
-use Symfony\Component\Config\Definition\Exception\TreeWithoutRootNodeException;
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass;
use Symfony\Component\DependencyInjection\Compiler\RegisterEnvVarProcessorsPass;
use Symfony\Component\DependencyInjection\Compiler\ValidateEnvPlaceholdersPass;
@@ -56,12 +55,11 @@ public function testDefaultEnvIsValidatedInConfig()
$this->doProcess($container);
}
- /**
- * @group legacy
- * @expectedDeprecation A non-string default value of an env() parameter is deprecated since 4.3, cast "env(FLOATISH)" to string instead.
- */
public function testDefaultEnvWithoutPrefixIsValidatedInConfig()
{
+ $this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException');
+ $this->expectExceptionMessage('The default value of an env() parameter must be a string or null, but "double" given to "env(FLOATISH)".');
+
$container = new ContainerBuilder();
$container->setParameter('env(FLOATISH)', 3.2);
$container->registerExtension($ext = new EnvExtension());
@@ -70,8 +68,6 @@ public function testDefaultEnvWithoutPrefixIsValidatedInConfig()
]);
$this->doProcess($container);
-
- $this->assertSame($expected, $container->resolveEnvPlaceholders($ext->getConfig()));
}
public function testEnvsAreValidatedInConfigWithInvalidPlaceholder()
@@ -219,14 +215,11 @@ public function testEmptyEnvWhichCannotBeEmptyForScalarNode(): void
$this->assertSame($expected, $container->resolveEnvPlaceholders($ext->getConfig()));
}
- /**
- * NOT LEGACY (test exception in 5.0).
- *
- * @group legacy
- * @expectedDeprecation Setting path "env_extension.scalar_node_not_empty_validated" to an environment variable is deprecated since Symfony 4.3. Remove "cannotBeEmpty()", "validate()" or include a prefix/suffix value instead.
- */
public function testEmptyEnvWhichCannotBeEmptyForScalarNodeWithValidation(): void
{
+ $this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
+ $this->expectExceptionMessage('The path "env_extension.scalar_node_not_empty_validated" cannot contain an environment variable when empty values are not allowed by definition and are validated.');
+
$container = new ContainerBuilder();
$container->registerExtension($ext = new EnvExtension());
$container->prependExtensionConfig('env_extension', $expected = [
@@ -234,8 +227,6 @@ public function testEmptyEnvWhichCannotBeEmptyForScalarNodeWithValidation(): voi
]);
$this->doProcess($container);
-
- $this->assertSame($expected, $container->resolveEnvPlaceholders($ext->getConfig()));
}
public function testPartialEnvWhichCannotBeEmptyForScalarNode(): void
@@ -264,19 +255,6 @@ public function testEnvWithVariableNode(): void
$this->assertSame($expected, $container->resolveEnvPlaceholders($ext->getConfig()));
}
- /**
- * @group legacy
- * @expectedDeprecation A tree builder without a root node is deprecated since Symfony 4.2 and will not be supported anymore in 5.0.
- */
- public function testConfigurationWithoutRootNode(): void
- {
- $container = new ContainerBuilder();
- $container->registerExtension(new EnvExtension(new EnvConfigurationWithoutRootNode()));
- $container->loadFromExtension('env_extension', ['foo' => 'bar']);
-
- (new ValidateEnvPlaceholdersPass())->process($container);
- }
-
public function testEmptyConfigFromMoreThanOneSource()
{
$container = new ContainerBuilder();
@@ -374,14 +352,6 @@ public function getConfigTreeBuilder(): TreeBuilder
}
}
-class EnvConfigurationWithoutRootNode implements ConfigurationInterface
-{
- public function getConfigTreeBuilder(): TreeBuilder
- {
- return new TreeBuilder();
- }
-}
-
class ConfigurationWithArrayNodeRequiringOneElement implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
@@ -426,11 +396,7 @@ public function load(array $configs, ContainerBuilder $container)
return;
}
- try {
- $this->config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs);
- } catch (TreeWithoutRootNodeException $e) {
- $this->config = null;
- }
+ $this->config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs);
}
public function getConfig()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index 5a3cd6c7e4d0f..5763b1d4f56c5 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -22,6 +22,7 @@
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -41,6 +42,7 @@
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy;
use Symfony\Component\DependencyInjection\TypedReference;
@@ -542,6 +544,18 @@ public function testCreateServiceWithExpression()
$this->assertEquals('foobar', $builder->get('foo')->arguments['foo']);
}
+ public function testCreateServiceWithAbstractArgument()
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Argument "$baz" of service "foo" is abstract (should be defined by Pass), did you forget to define it?');
+
+ $builder = new ContainerBuilder();
+ $builder->register('foo', FooWithAbstractArgument::class)
+ ->addArgument(new AbstractArgument('foo', '$baz', 'should be defined by Pass'));
+
+ $builder->get('foo');
+ }
+
public function testResolveServices()
{
$builder = new ContainerBuilder();
@@ -1601,6 +1615,24 @@ public function testWither()
$wither = $container->get('wither');
$this->assertInstanceOf(Foo::class, $wither->foo);
}
+
+ public function testAutoAliasing()
+ {
+ $container = new ContainerBuilder();
+ $container->register(C::class);
+ $container->register(D::class);
+
+ $container->setParameter('foo', D::class);
+
+ $definition = new Definition(X::class);
+ $definition->setPublic(true);
+ $definition->addTag('auto_alias', ['format' => '%foo%']);
+ $container->setDefinition(X::class, $definition);
+
+ $container->compile();
+
+ $this->assertInstanceOf(D::class, $container->get(X::class));
+ }
}
class FooClass
@@ -1617,3 +1649,15 @@ public function __construct(A $a)
{
}
}
+
+interface X
+{
+}
+
+class C implements X
+{
+}
+
+class D implements X
+{
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
index 9015bf6b4f666..7050c6a22b820 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
@@ -184,7 +184,7 @@ public function testSetWithNullOnUninitializedPredefinedService()
{
$sc = new Container();
$sc->set('foo', new \stdClass());
- $sc->get('foo', null);
+ $sc->get('foo');
$sc->set('foo', null);
$this->assertFalse($sc->has('foo'), '->set() with null service resets the service');
@@ -288,19 +288,6 @@ public function testHas()
$this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined');
}
- /**
- * @group legacy
- */
- public function testScalarService()
- {
- $c = new Container();
-
- $c->set('foo', 'some value');
-
- $this->assertTrue($c->has('foo'));
- $this->assertSame('some value', $c->get('foo'));
- }
-
public function testInitialized()
{
$sc = new ProjectServiceContainer();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
index f67cdd520e709..aad4fdf51432d 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
@@ -14,7 +14,6 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
class DefinitionTest extends TestCase
@@ -29,18 +28,6 @@ public function testConstructor()
$this->assertEquals(['foo'], $def->getArguments(), '__construct() takes an optional array of arguments as its second argument');
}
- /**
- * @group legacy
- * @expectedDeprecation Passing an instance of Symfony\Component\DependencyInjection\Parameter as class name to Symfony\Component\DependencyInjection\Definition in deprecated in Symfony 4.4 and will result in a TypeError in 5.0. Please pass the string "%parameter%" instead.
- */
- public function testConstructorWithParameter()
- {
- $parameter = new Parameter('parameter');
-
- $def = new Definition($parameter);
- $this->assertSame($parameter, $def->getClass(), '__construct() accepts Parameter instances');
- }
-
public function testSetGetFactory()
{
$def = new Definition();
@@ -63,28 +50,6 @@ public function testSetGetClass()
$this->assertEquals('foo', $def->getClass(), '->getClass() returns the class name');
}
- /**
- * @group legacy
- * @expectedDeprecation Passing an instance of Symfony\Component\DependencyInjection\Parameter as class name to Symfony\Component\DependencyInjection\Definition in deprecated in Symfony 4.4 and will result in a TypeError in 5.0. Please pass the string "%parameter%" instead.
- */
- public function testSetGetClassWithParameter()
- {
- $def = new Definition();
- $parameter = new Parameter('parameter');
- $this->assertSame($parameter, $def->setClass($parameter)->getClass(), '->getClass() returns the parameterized class name');
- }
-
- /**
- * @group legacy
- * @expectedDeprecation The class name passed to Symfony\Component\DependencyInjection\Definition is expected to be a string. Passing a stdClass is deprecated in Symfony 4.4 and will result in a TypeError in 5.0.
- */
- public function testSetGetClassWithObject()
- {
- $def = new Definition();
- $classObject = new \stdClass();
- $this->assertSame($classObject, $def->setClass($classObject)->getClass(), '->getClass() returns the parameterized class name');
- }
-
public function testSetGetDecoratedService()
{
$def = new Definition('stdClass');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 60f1ab1d4f070..0fbd9f08eadd7 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -15,6 +15,7 @@
use Psr\Container\ContainerInterface;
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -33,6 +34,7 @@
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
@@ -227,7 +229,7 @@ public function testDumpAsFiles()
->addError('No-no-no-no');
$container->compile();
$dumper = new PhpDumper($container);
- $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot']), true);
+ $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot', 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]), true);
if ('\\' === \DIRECTORY_SEPARATOR) {
$dump = str_replace('\\\\Fixtures\\\\includes\\\\foo.php', '/Fixtures/includes/foo.php', $dump);
}
@@ -297,7 +299,7 @@ public function testNonSharedLazyDumpAsFiles()
->setLazy(true);
$container->compile();
$dumper = new PhpDumper($container);
- $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__]), true);
+ $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]), true);
if ('\\' === \DIRECTORY_SEPARATOR) {
$dump = str_replace('\\\\Fixtures\\\\includes\\\\foo_lazy.php', '/Fixtures/includes/foo_lazy.php', $dump);
@@ -478,7 +480,7 @@ public function testEnvParameter()
$container->compile();
$dumper = new PhpDumper($container);
- $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_EnvParameters', 'file' => self::$fixturesPath.'/php/services26.php']));
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_EnvParameters', 'file' => self::$fixturesPath.'/php/services26.php', 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]));
require self::$fixturesPath.'/php/services26.php';
$container = new \Symfony_DI_PhpDumper_Test_EnvParameters();
@@ -944,7 +946,7 @@ public function testArrayParameters()
$dumper = new PhpDumper($container);
- $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_array_params.php', str_replace('\\\\Dumper', '/Dumper', $dumper->dump(['file' => self::$fixturesPath.'/php/services_array_params.php'])));
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_array_params.php', str_replace('\\\\Dumper', '/Dumper', $dumper->dump(['file' => self::$fixturesPath.'/php/services_array_params.php', 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false])));
}
public function testExpressionReferencingPrivateService()
@@ -1363,11 +1365,29 @@ public function testMultipleDeprecatedAliasesWorking()
$this->assertInstanceOf(\stdClass::class, $container->get('deprecated1'));
$this->assertInstanceOf(\stdClass::class, $container->get('deprecated2'));
}
+
+ public function testDumpServiceWithAbstractArgument()
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Argument "$baz" of service "Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument" is abstract (should be defined by Pass), did you forget to define it?');
+
+ $container = new ContainerBuilder();
+
+ $container->register(FooWithAbstractArgument::class, FooWithAbstractArgument::class)
+ ->setArgument('$baz', new AbstractArgument(FooWithAbstractArgument::class, '$baz', 'should be defined by Pass'))
+ ->setArgument('$bar', 'test')
+ ->setPublic(true);
+
+ $container->compile();
+
+ $dumper = new PhpDumper($container);
+ $dumper->dump();
+ }
}
class Rot13EnvVarProcessor implements EnvVarProcessorInterface
{
- public function getEnv($prefix, $name, \Closure $getEnv)
+ public function getEnv(string $prefix, string $name, \Closure $getEnv)
{
return str_rot13($getEnv($name));
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php
index 447278282056a..ced09157617c3 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -20,6 +21,7 @@
use Symfony\Component\DependencyInjection\Dumper\XmlDumper;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
class XmlDumperTest extends TestCase
{
@@ -237,4 +239,15 @@ public function testDumpAbstractServices()
$this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services_abstract.xml'), $dumper->dump());
}
+
+ public function testDumpServiceWithAbstractArgument()
+ {
+ $container = new ContainerBuilder();
+ $container->register(FooWithAbstractArgument::class, FooWithAbstractArgument::class)
+ ->setArgument('$baz', new AbstractArgument(FooWithAbstractArgument::class, '$baz', 'should be defined by Pass'))
+ ->setArgument('$bar', 'test');
+
+ $dumper = new XmlDumper($container);
+ $this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_with_abstract_argument.xml', $dumper->dump());
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php
index 7a83b2e6a1b8d..d7896ae436619 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -21,6 +22,7 @@
use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Yaml;
@@ -117,6 +119,17 @@ public function testTaggedArguments()
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_tagged_argument.yml', $dumper->dump());
}
+ public function testDumpServiceWithAbstractArgument()
+ {
+ $container = new ContainerBuilder();
+ $container->register(FooWithAbstractArgument::class, FooWithAbstractArgument::class)
+ ->setArgument('$baz', new AbstractArgument(FooWithAbstractArgument::class, '$baz', 'should be defined by Pass'))
+ ->setArgument('$bar', 'test');
+
+ $dumper = new YamlDumper($container);
+ $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_abstract_argument.yml', $dumper->dump());
+ }
+
private function assertEqualYamlStructure(string $expected, string $yaml, string $message = '')
{
$parser = new Parser();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php b/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php
index 16a1d39e2940f..452fb79727637 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php
@@ -14,6 +14,10 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\Extension\InvalidConfig\InvalidConfigExtension;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\Extension\SemiValidConfig\SemiValidConfigExtension;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\Extension\ValidConfig\Configuration;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\Extension\ValidConfig\ValidConfigExtension;
class ExtensionTest extends TestCase
{
@@ -42,6 +46,37 @@ public function testIsConfigEnabledOnNonEnableableConfig()
$extension->isConfigEnabled(new ContainerBuilder(), []);
}
+
+ public function testNoConfiguration()
+ {
+ $extension = new EnableableExtension();
+
+ $this->assertNull($extension->getConfiguration([], new ContainerBuilder()));
+ }
+
+ public function testValidConfiguration()
+ {
+ $extension = new ValidConfigExtension();
+
+ $this->assertInstanceOf(Configuration::class, $extension->getConfiguration([], new ContainerBuilder()));
+ }
+
+ public function testSemiValidConfiguration()
+ {
+ $extension = new SemiValidConfigExtension();
+
+ $this->assertNull($extension->getConfiguration([], new ContainerBuilder()));
+ }
+
+ public function testInvalidConfiguration()
+ {
+ $this->expectException('Symfony\Component\DependencyInjection\Exception\LogicException');
+ $this->expectExceptionMessage('The extension configuration class "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\Extension\\InvalidConfig\\Configuration" must implement "Symfony\\Component\\Config\\Definition\\ConfigurationInterface".');
+
+ $extension = new InvalidConfigExtension();
+
+ $extension->getConfiguration([], new ContainerBuilder());
+ }
}
class EnableableExtension extends Extension
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/DeprecatedClass.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/DeprecatedClass.php
index 33f37a0304f92..78e891eaafe46 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/DeprecatedClass.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/DeprecatedClass.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
-@trigger_error('deprecated', E_USER_DEPRECATED);
+trigger_deprecation('', '', 'deprecated');
class DeprecatedClass
{
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Extension/InvalidConfig/Configuration.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Extension/InvalidConfig/Configuration.php
new file mode 100644
index 0000000000000..bcb2995f17758
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Extension/InvalidConfig/Configuration.php
@@ -0,0 +1,7 @@
+baz = $baz;
+ $this->bar = $bar;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
index b88e0d73565e8..2a2c19d1adc35 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
@@ -279,7 +279,7 @@ public function setFoo(Foo $foo)
* @required
* @return static
*/
- public function withFoo1(Foo $foo)
+ public function withFoo1(Foo $foo): self
{
return $this->withFoo2($foo);
}
@@ -288,7 +288,7 @@ public function withFoo1(Foo $foo)
* @required
* @return static
*/
- public function withFoo2(Foo $foo)
+ public function withFoo2(Foo $foo): self
{
$new = clone $this;
$new->foo = $foo;
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php
new file mode 100644
index 0000000000000..f1d76f2f0c788
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php
@@ -0,0 +1,15 @@
+isLazy();
}
- public function getProxyFactoryCode(Definition $definition, $id, $factoryCall = null): string
+ public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode): string
{
return " // lazy factory for {$definition->getClass()}\n\n";
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php
index 3434cfc61847f..a7cf90e1a57b1 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php
@@ -66,7 +66,7 @@ protected function getFooService()
*/
protected function getAliasForFooDeprecatedService()
{
- @trigger_error('The "alias_for_foo_deprecated" service alias is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "alias_for_foo_deprecated" service alias is deprecated. You should stop using it, as it will be removed in the future.');
return $this->get('foo');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php
index bc5a096746a8a..d54782c7c6b3a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php
@@ -59,11 +59,9 @@ protected function getTestService()
return $this->services['test'] = new \stdClass(['only dot' => '.', 'concatenation as value' => '.\'\'.', 'concatenation from the start value' => '\'\'.', '.' => 'dot as a key', '.\'\'.' => 'concatenation as a key', '\'\'.' => 'concatenation from the start key', 'optimize concatenation' => 'string1-string2', 'optimize concatenation with empty string' => 'string1string2', 'optimize concatenation from the start' => 'start', 'optimize concatenation at the end' => 'end', 'new line' => 'string with '."\n".'new line']);
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -73,14 +71,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php
index 3dfa8bdd6d0ef..ffab1abb1deba 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php
@@ -59,11 +59,9 @@ protected function getTestService()
return $this->services['test'] = new \stdClass(('wiz'.\dirname(__DIR__, 1)), [('wiz'.\dirname(__DIR__, 1)) => (\dirname(__DIR__, 2).'/')]);
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -73,14 +71,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
index b5ff25acb81a7..2c74240ac36d0 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
@@ -74,11 +74,9 @@ protected function getServiceWithMethodCallAndFactoryService()
return $instance;
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -88,14 +86,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php
index 063ebfd14d47e..29b3627def9c6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php
@@ -70,11 +70,9 @@ protected function getTestService()
return $this->services['test'] = new ${($_ = $this->getEnv('FOO')) && false ?: "_"}($this->getEnv('Bar'), 'foo'.$this->getEnv('string:FOO').'baz', $this->getEnv('int:Baz'));
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -84,14 +82,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php
index aefde3a4734ac..d4dae984b68e2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt
index 2bbf46c906b95..286d0bf8ae966 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt
@@ -124,7 +124,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'deprecated_service' shared service.
-@trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+trigger_deprecation('', '', 'The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.');
return $this->services['deprecated_service'] = new \stdClass();
@@ -156,7 +156,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'factory_simple' shared service.
-@trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+trigger_deprecation('', '', 'The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.');
return new \SimpleFactoryClass('foo');
@@ -460,14 +460,13 @@ class ProjectServiceContainer extends Container
return $instance;
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return $this->buildParameters[$name];
}
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -477,17 +476,16 @@ class ProjectServiceContainer extends Container
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return true;
}
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
index 512994ef1210b..d448e7d5bcf8a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
@@ -210,7 +210,7 @@ protected function getDecoratorServiceWithNameService()
*/
protected function getDeprecatedServiceService()
{
- @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.');
return $this->services['deprecated_service'] = new \stdClass();
}
@@ -404,16 +404,14 @@ protected function getTaggedIteratorService()
*/
protected function getFactorySimpleService()
{
- @trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.');
return new \SimpleFactoryClass('foo');
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -423,14 +421,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt
index 808a1c5d07ad9..218f7ea131c78 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt
@@ -231,7 +231,7 @@ class ProjectServiceContainer extends Container
*/
protected function getDeprecatedServiceService()
{
- @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.');
return $this->services['deprecated_service'] = new \stdClass();
}
@@ -453,19 +453,18 @@ class ProjectServiceContainer extends Container
*/
protected function getFactorySimpleService()
{
- @trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.');
return new \SimpleFactoryClass('foo');
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return $this->buildParameters[$name];
}
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -475,17 +474,16 @@ class ProjectServiceContainer extends Container
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return true;
}
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
index 2b5409a39fef1..65c9113674000 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
@@ -94,14 +94,13 @@ class ProjectServiceContainer extends Container
return new \Bar\FooClass(new \Bar\FooLazyClass());
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return $this->buildParameters[$name];
}
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -111,17 +110,16 @@ class ProjectServiceContainer extends Container
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
if (isset($this->buildParameters[$name])) {
return true;
}
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php
index 4e704e469ccb4..71cb9be712a9d 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php
@@ -63,11 +63,9 @@ protected function getBarService()
return $instance;
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -77,14 +75,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php
index fe5a60a0dfa11..5514ceb61f912 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php
index 4fbb83188507a..152933f61ebd4 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_csv_env.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php
index 582ac453af187..a5a29f678fe7a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php
index 3d381cabd14e8..2ba429b2eaef1 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php
@@ -72,11 +72,9 @@ protected function getFooService()
return $this->services['foo'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? ($this->privates['bar_%env(BAR)%'] = new \stdClass())), ['baz_'.$this->getEnv('string:BAR') => new \stdClass()]);
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -86,14 +84,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php
index 3e84e304a762e..1e7ce2f2c95c8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php
@@ -210,7 +210,7 @@ protected function getDecoratorServiceWithNameService()
*/
protected function getDeprecatedServiceService()
{
- @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "deprecated_service" service is deprecated. You should stop using it, as it will be removed in the future.');
return $this->services['deprecated_service'] = new \stdClass();
}
@@ -404,16 +404,14 @@ protected function getTaggedIteratorService()
*/
protected function getFactorySimpleService()
{
- @trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.', E_USER_DEPRECATED);
+ trigger_deprecation('', '', 'The "factory_simple" service is deprecated. You should stop using it, as it will be removed in the future.');
return new \SimpleFactoryClass('foo');
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -423,14 +421,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php
index 14bbc1ff31090..a5b2846202df2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php
@@ -92,11 +92,9 @@ protected function getC2Service()
return $this->services['Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3());
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -106,14 +104,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_json_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_json_env.php
index 18b525309258e..1d7f3686bb108 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_json_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_json_env.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_query_string_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_query_string_env.php
index be3d8dcadbd9c..1aa1c51c9ddb1 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_query_string_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_query_string_env.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php
index 5dcfa4159e14c..b18eeaaaf8315 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php
@@ -77,11 +77,9 @@ protected function getContainer_EnvVarProcessorsLocatorService()
]);
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -91,14 +89,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
index e184fad7bdf0c..4f9074e9aaf7f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
@@ -45,8 +45,8 @@ public function isCompiled(): bool
public function getRemovedIds(): array
{
return [
- '.service_locator.bPEFRiK' => true,
- '.service_locator.bPEFRiK.foo_service' => true,
+ '.service_locator.2Wk0Efb' => true,
+ '.service_locator.2Wk0Efb.foo_service' => true,
'Psr\\Container\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true,
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_unsupported_characters.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_unsupported_characters.php
index a958e8ee05557..0b2540cdb8d9e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_unsupported_characters.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_unsupported_characters.php
@@ -81,11 +81,9 @@ protected function getFooohnoService()
return $this->services['foo*/oh-no'] = new \FooClass();
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -95,14 +93,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_url_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_url_env.php
index 1ef02e372c97e..df4648dd3d258 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_url_env.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_url_env.php
@@ -46,11 +46,9 @@ public function getRemovedIds(): array
];
}
- public function getParameter($name)
+ public function getParameter(string $name)
{
- $name = (string) $name;
-
- if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
+ if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
}
if (isset($this->loadedDynamicParameters[$name])) {
@@ -60,14 +58,12 @@ public function getParameter($name)
return $this->parameters[$name];
}
- public function hasParameter($name): bool
+ public function hasParameter(string $name): bool
{
- $name = (string) $name;
-
- return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
+ return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters);
}
- public function setParameter($name, $value): void
+ public function setParameter(string $name, $value): void
{
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/service_with_abstract_argument.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/service_with_abstract_argument.xml
new file mode 100644
index 0000000000000..c7ba1950761d4
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/service_with_abstract_argument.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ should be defined by FooCompilerPass
+ test
+
+
+
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_abstract_argument.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_abstract_argument.xml
new file mode 100644
index 0000000000000..8a05525f555aa
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_abstract_argument.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ should be defined by Pass
+ test
+
+
+
+
+
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_abstract_argument.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_abstract_argument.yml
new file mode 100644
index 0000000000000..02889228c03cf
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_abstract_argument.yml
@@ -0,0 +1,15 @@
+
+services:
+ service_container:
+ class: Symfony\Component\DependencyInjection\ContainerInterface
+ public: true
+ synthetic: true
+ Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument:
+ class: Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument
+ arguments: { $baz: !abstract 'should be defined by Pass', $bar: test }
+ Psr\Container\ContainerInterface:
+ alias: service_container
+ public: false
+ Symfony\Component\DependencyInjection\ContainerInterface:
+ alias: service_container
+ public: false
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
index 57ea37e6f4964..9c368e23aba5c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
@@ -244,12 +244,12 @@ class TestFileLoader extends FileLoader
{
public $autoRegisterAliasesForSinglyImplementedInterfaces = true;
- public function load($resource, $type = null)
+ public function load($resource, string $type = null)
{
return $resource;
}
- public function supports($resource, $type = null): bool
+ public function supports($resource, string $type = null): bool
{
return false;
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index 14ba4c7f6e567..55831540aa8d6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -16,6 +16,7 @@
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Resource\GlobResource;
+use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
@@ -31,6 +32,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar;
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
use Symfony\Component\ExpressionLanguage\Expression;
@@ -992,4 +994,15 @@ public function testNotSinglyImplementedInterfacesInMultipleResourcesWithPreviou
$this->assertSame(Prototype\SinglyImplementedInterface\Adapter\Adapter::class, (string) $alias);
}
+
+ public function testLoadServiceWithAbstractArgument()
+ {
+ $container = new ContainerBuilder();
+ $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
+ $loader->load('service_with_abstract_argument.xml');
+
+ $this->assertTrue($container->hasDefinition(FooWithAbstractArgument::class));
+ $arguments = $container->getDefinition(FooWithAbstractArgument::class)->getArguments();
+ $this->assertInstanceOf(AbstractArgument::class, $arguments['$baz']);
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index fc827df18cd4f..d2e81db377313 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -216,21 +216,6 @@ public function testDeprecatedAliases()
$this->assertSame($message, $container->getAlias('alias_for_foobar')->getDeprecationMessage('alias_for_foobar'));
}
- /**
- * @group legacy
- */
- public function testLoadFactoryShortSyntax()
- {
- $container = new ContainerBuilder();
- $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
- $loader->load('services14.yml');
- $services = $container->getDefinitions();
-
- $this->assertEquals([new Reference('baz'), 'getClass'], $services['factory']->getFactory(), '->load() parses the factory tag with service:method');
- $this->assertEquals(['FooBacFactory', 'createFooBar'], $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method');
- $this->assertEquals([new Reference('factory'), '__invoke'], $services['invokable_factory']->getFactory(), '->load() parses string service reference');
- }
-
public function testFactorySyntaxError()
{
$container = new ContainerBuilder();
@@ -240,20 +225,6 @@ public function testFactorySyntaxError()
$loader->load('bad_factory_syntax.yml');
}
- /**
- * @group legacy
- */
- public function testLoadConfiguratorShortSyntax()
- {
- $container = new ContainerBuilder();
- $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
- $loader->load('services_configurator_short_syntax.yml');
- $services = $container->getDefinitions();
-
- $this->assertEquals([new Reference('foo_bar_configurator'), 'configure'], $services['foo_bar']->getConfigurator(), '->load() parses the configurator tag with service:method');
- $this->assertEquals(['FooBarConfigurator', 'configureFooBar'], $services['foo_bar_with_static_call']->getConfigurator(), '->load() parses the configurator tag with Class::method');
- }
-
public function testExtensions()
{
$container = new ContainerBuilder();
@@ -341,11 +312,8 @@ public function testTaggedArgumentsWithIndex()
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', true, 'getPriority');
$this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_service_tagged_locator')->getArgument(0));
- if (is_subclass_of('Symfony\Component\Yaml\Exception\ExceptionInterface', 'Throwable')) {
- // this test is not compatible with Yaml v3
- $taggedIterator = new TaggedIteratorArgument('foo', null, null, true);
- $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('bar_service_tagged_locator')->getArgument(0));
- }
+ $taggedIterator = new TaggedIteratorArgument('foo', null, null, true);
+ $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('bar_service_tagged_locator')->getArgument(0));
}
public function testNameOnlyTagsAreAllowedAsString()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php
index 25fd61cca2a2a..ea6e3814af1d6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php
@@ -109,33 +109,25 @@ public function testMergeWithDifferentIdentifiersForPlaceholders()
$this->assertCount(2, $merged[$envName]);
}
- /**
- * @group legacy
- * @expectedDeprecation A non-string default value of env parameter "INT_VAR" is deprecated since 4.3, cast it to string instead.
- */
- public function testResolveEnvCastsIntToString()
+ public function testResolveEnvRequiresStrings()
{
+ $this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException');
+ $this->expectExceptionMessage('The default value of env parameter "INT_VAR" must be a string or null, integer given.');
+
$bag = new EnvPlaceholderParameterBag();
$bag->get('env(INT_VAR)');
$bag->set('env(INT_VAR)', 2);
$bag->resolve();
- $this->assertSame('2', $bag->all()['env(INT_VAR)']);
}
- /**
- * @group legacy
- * @expectedDeprecation A non-string default value of an env() parameter is deprecated since 4.3, cast "env(INT_VAR)" to string instead.
- * @expectedDeprecation A non-string default value of env parameter "INT_VAR" is deprecated since 4.3, cast it to string instead.
- */
public function testGetDefaultScalarEnv()
{
+ $this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException');
+ $this->expectExceptionMessage('The default value of an env() parameter must be a string or null, but "integer" given to "env(INT_VAR)".');
+
$bag = new EnvPlaceholderParameterBag();
$bag->set('env(INT_VAR)', 2);
- $this->assertStringMatchesFormat('env_%s_INT_VAR_%s', $bag->get('env(INT_VAR)'));
- $this->assertSame(2, $bag->all()['env(INT_VAR)']);
- $bag->resolve();
- $this->assertStringMatchesFormat('env_%s_INT_VAR_%s', $bag->get('env(INT_VAR)'));
- $this->assertSame('2', $bag->all()['env(INT_VAR)']);
+ $bag->get('env(INT_VAR)');
}
public function testGetDefaultEnv()
@@ -162,7 +154,7 @@ public function testResolveEnvAllowsNull()
public function testResolveThrowsOnBadDefaultValue()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException');
- $this->expectExceptionMessage('The default value of env parameter "ARRAY_VAR" must be scalar or null, array given.');
+ $this->expectExceptionMessage('The default value of env parameter "ARRAY_VAR" must be a string or null, array given.');
$bag = new EnvPlaceholderParameterBag();
$bag->get('env(ARRAY_VAR)');
$bag->set('env(ARRAY_VAR)', []);
@@ -182,7 +174,7 @@ public function testGetEnvAllowsNull()
public function testGetThrowsOnBadDefaultValue()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException');
- $this->expectExceptionMessage('The default value of an env() parameter must be scalar or null, but "array" given to "env(ARRAY_VAR)".');
+ $this->expectExceptionMessage('The default value of an env() parameter must be a string or null, but "array" given to "env(ARRAY_VAR)".');
$bag = new EnvPlaceholderParameterBag();
$bag->set('env(ARRAY_VAR)', []);
$bag->get('env(ARRAY_VAR)');
@@ -195,4 +187,11 @@ public function testDefaultToNullAllowed()
$bag->resolve();
$this->assertNotNull($bag->get('env(default::BAR)'));
}
+
+ public function testExtraCharsInProcessor()
+ {
+ $bag = new EnvPlaceholderParameterBag();
+ $bag->resolve();
+ $this->assertStringMatchesFormat('env_%s_key_a_b_c_FOO_%s', $bag->get('env(key:a.b-c:FOO)'));
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/TypedReference.php b/src/Symfony/Component/DependencyInjection/TypedReference.php
index 15b2b706c169c..78d8edf0b03f6 100644
--- a/src/Symfony/Component/DependencyInjection/TypedReference.php
+++ b/src/Symfony/Component/DependencyInjection/TypedReference.php
@@ -20,7 +20,6 @@ class TypedReference extends Reference
{
private $type;
private $name;
- private $requiringClass;
/**
* @param string $id The service identifier
@@ -28,16 +27,9 @@ class TypedReference extends Reference
* @param int $invalidBehavior The behavior when the service does not exist
* @param string $name The name of the argument targeting the service
*/
- public function __construct(string $id, string $type, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name = null)
+ public function __construct(string $id, string $type, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, string $name = null)
{
- if (\is_string($invalidBehavior ?? '') || \is_int($name)) {
- @trigger_error(sprintf('Passing the $requiringClass as 3rd argument for "%s()" is deprecated since Symfony 4.1. It should be removed, moving all following arguments 1 to the left.', __METHOD__), E_USER_DEPRECATED);
-
- $this->requiringClass = $invalidBehavior;
- $invalidBehavior = 3 < \func_num_args() ? func_get_arg(3) : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
- } else {
- $this->name = $type === $id ? $name : null;
- }
+ $this->name = $type === $id ? $name : null;
parent::__construct($id, $invalidBehavior);
$this->type = $type;
}
@@ -51,24 +43,4 @@ public function getName(): ?string
{
return $this->name;
}
-
- /**
- * @deprecated since Symfony 4.1
- */
- public function getRequiringClass()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->requiringClass ?? '';
- }
-
- /**
- * @deprecated since Symfony 4.1
- */
- public function canBeAutoregistered()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->requiringClass && (false !== $i = strpos($this->type, '\\')) && 0 === strncasecmp($this->type, $this->requiringClass, 1 + $i);
- }
}
diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json
index df1727b9d08e5..12d6febe95034 100644
--- a/src/Symfony/Component/DependencyInjection/composer.json
+++ b/src/Symfony/Component/DependencyInjection/composer.json
@@ -16,14 +16,15 @@
}
],
"require": {
- "php": "^7.1.3",
+ "php": "^7.2.5",
"psr/container": "^1.0",
+ "symfony/deprecation-contracts": "^2.1",
"symfony/service-contracts": "^1.1.6|^2"
},
"require-dev": {
- "symfony/yaml": "^3.4|^4.0|^5.0",
- "symfony/config": "^4.3",
- "symfony/expression-language": "^3.4|^4.0|^5.0"
+ "symfony/yaml": "^4.4|^5.0",
+ "symfony/config": "^5.0",
+ "symfony/expression-language": "^4.4|^5.0"
},
"suggest": {
"symfony/yaml": "",
@@ -33,10 +34,10 @@
"symfony/proxy-manager-bridge": "Generate service proxies to lazy load them"
},
"conflict": {
- "symfony/config": "<4.3|>=5.0",
- "symfony/finder": "<3.4",
- "symfony/proxy-manager-bridge": "<3.4",
- "symfony/yaml": "<3.4"
+ "symfony/config": "<5.0",
+ "symfony/finder": "<4.4",
+ "symfony/proxy-manager-bridge": "<4.4",
+ "symfony/yaml": "<4.4"
},
"provide": {
"psr/container-implementation": "1.0",
@@ -51,7 +52,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/DomCrawler/AbstractUriElement.php b/src/Symfony/Component/DomCrawler/AbstractUriElement.php
index ead9dca25bacb..2afc822c57d25 100644
--- a/src/Symfony/Component/DomCrawler/AbstractUriElement.php
+++ b/src/Symfony/Component/DomCrawler/AbstractUriElement.php
@@ -80,46 +80,7 @@ public function getMethod()
*/
public function getUri()
{
- $uri = trim($this->getRawUri());
-
- // absolute URL?
- if (null !== parse_url($uri, PHP_URL_SCHEME)) {
- return $uri;
- }
-
- // empty URI
- if (!$uri) {
- return $this->currentUri;
- }
-
- // an anchor
- if ('#' === $uri[0]) {
- return $this->cleanupAnchor($this->currentUri).$uri;
- }
-
- $baseUri = $this->cleanupUri($this->currentUri);
-
- if ('?' === $uri[0]) {
- return $baseUri.$uri;
- }
-
- // absolute URL with relative schema
- if (0 === strpos($uri, '//')) {
- return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri;
- }
-
- $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri);
-
- // absolute path
- if ('/' === $uri[0]) {
- return $baseUri.$uri;
- }
-
- // relative path
- $path = parse_url(substr($this->currentUri, \strlen($baseUri)), PHP_URL_PATH);
- $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
-
- return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path;
+ return UriResolver::resolve($this->getRawUri(), $this->currentUri);
}
/**
@@ -136,7 +97,7 @@ abstract protected function getRawUri();
*
* @return string
*/
- protected function canonicalizePath($path)
+ protected function canonicalizePath(string $path)
{
if ('' === $path || '/' === $path) {
return $path;
@@ -167,36 +128,4 @@ protected function canonicalizePath($path)
* @throws \LogicException If given node is not an anchor
*/
abstract protected function setNode(\DOMElement $node);
-
- /**
- * Removes the query string and the anchor from the given uri.
- */
- private function cleanupUri(string $uri): string
- {
- return $this->cleanupQuery($this->cleanupAnchor($uri));
- }
-
- /**
- * Remove the query string from the uri.
- */
- private function cleanupQuery(string $uri): string
- {
- if (false !== $pos = strpos($uri, '?')) {
- return substr($uri, 0, $pos);
- }
-
- return $uri;
- }
-
- /**
- * Remove the anchor from the uri.
- */
- private function cleanupAnchor(string $uri): string
- {
- if (false !== $pos = strpos($uri, '#')) {
- return substr($uri, 0, $pos);
- }
-
- return $uri;
- }
}
diff --git a/src/Symfony/Component/DomCrawler/CHANGELOG.md b/src/Symfony/Component/DomCrawler/CHANGELOG.md
index 56863b7c73735..49d585f83ed6b 100644
--- a/src/Symfony/Component/DomCrawler/CHANGELOG.md
+++ b/src/Symfony/Component/DomCrawler/CHANGELOG.md
@@ -1,6 +1,18 @@
CHANGELOG
=========
+5.1.0
+-----
+
+* Added an internal cache layer on top of the CssSelectorConverter
+* Added `UriResolver` to resolve an URI according to a base URI
+
+5.0.0
+-----
+
+* Added argument `$selector` to ``Crawler::children()`
+* Added argument `$default` to ``Crawler::text()` and `html()`
+
4.4.0
-----
diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php
index e89476f1229dc..cf5a9ea7427d9 100644
--- a/src/Symfony/Component/DomCrawler/Crawler.php
+++ b/src/Symfony/Component/DomCrawler/Crawler.php
@@ -132,11 +132,8 @@ public function add($node)
* If the charset is not set via the content type, it is assumed to be UTF-8,
* or ISO-8859-1 as a fallback, which is the default charset defined by the
* HTTP 1.1 specification.
- *
- * @param string $content A string to parse as HTML/XML
- * @param string|null $type The content type of the string
*/
- public function addContent($content, $type = null)
+ public function addContent(string $content, string $type = null)
{
if (empty($type)) {
$type = 0 === strpos($content, 'html5Parser && strspn($content, " \t\r\n") === stripos($content, '') ? $this->parseHtml5($content, $charset) : $this->parseXhtml($content, $charset);
@@ -217,13 +211,11 @@ public function addHtmlContent($content, $charset = 'UTF-8')
* and then, get the errors via libxml_get_errors(). Be
* sure to clear errors with libxml_clear_errors() afterward.
*
- * @param string $content The XML content
- * @param string $charset The charset
- * @param int $options Bitwise OR of the libxml option constants
- * LIBXML_PARSEHUGE is dangerous, see
- * http://symfony.com/blog/security-release-symfony-2-0-17-released
+ * @param int $options Bitwise OR of the libxml option constants
+ * LIBXML_PARSEHUGE is dangerous, see
+ * http://symfony.com/blog/security-release-symfony-2-0-17-released
*/
- public function addXmlContent($content, $charset = 'UTF-8', $options = LIBXML_NONET)
+ public function addXmlContent(string $content, string $charset = 'UTF-8', int $options = LIBXML_NONET)
{
// remove the default namespace if it's the only namespace to make XPath expressions simpler
if (!preg_match('/xmlns:/', $content)) {
@@ -316,11 +308,9 @@ public function addNode(\DOMNode $node)
/**
* Returns a node given its position in the node list.
*
- * @param int $position The position
- *
* @return static
*/
- public function eq($position)
+ public function eq(int $position)
{
if (isset($this->nodes[$position])) {
return $this->createSubCrawler($this->nodes[$position]);
@@ -358,12 +348,9 @@ public function each(\Closure $closure)
/**
* Slices the list of nodes by $offset and $length.
*
- * @param int $offset
- * @param int $length
- *
* @return static
*/
- public function slice($offset = 0, $length = null)
+ public function slice(int $offset = 0, int $length = null)
{
return $this->createSubCrawler(\array_slice($this->nodes, $offset, $length));
}
@@ -524,20 +511,13 @@ public function parents()
/**
* Returns the children nodes of the current selection.
*
- * @param string|null $selector An optional CSS selector to filter children
- *
* @return static
*
* @throws \InvalidArgumentException When current node is empty
* @throws \RuntimeException If the CssSelector Component is not available and $selector is provided
*/
- public function children(/* string $selector = null */)
+ public function children(string $selector = null)
{
- if (\func_num_args() < 1 && __CLASS__ !== static::class && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
- @trigger_error(sprintf('The "%s()" method will have a new "string $selector = null" argument in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- }
- $selector = 0 < \func_num_args() ? func_get_arg(0) : null;
-
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@@ -557,13 +537,11 @@ public function children(/* string $selector = null */)
/**
* Returns the attribute value of the first node of the list.
*
- * @param string $attribute The attribute name
- *
* @return string|null The attribute value or null if the attribute does not exist
*
* @throws \InvalidArgumentException When current node is empty
*/
- public function attr($attribute)
+ public function attr(string $attribute)
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -602,11 +580,11 @@ public function nodeName()
*
* @throws \InvalidArgumentException When current node is empty
*/
- public function text(/* string $default = null, bool $normalizeWhitespace = true */)
+ public function text(string $default = null, bool $normalizeWhitespace = true)
{
if (!$this->nodes) {
- if (0 < \func_num_args() && null !== func_get_arg(0)) {
- return (string) func_get_arg(0);
+ if (null !== $default) {
+ return $default;
}
throw new \InvalidArgumentException('The current node list is empty.');
@@ -614,15 +592,7 @@ public function text(/* string $default = null, bool $normalizeWhitespace = true
$text = $this->getNode(0)->nodeValue;
- if (\func_num_args() <= 1) {
- if (trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $text)) !== $text) {
- @trigger_error(sprintf('"%s()" will normalize whitespaces by default in Symfony 5.0, set the second "$normalizeWhitespace" argument to false to retrieve the non-normalized version of the text.', __METHOD__), E_USER_DEPRECATED);
- }
-
- return $text;
- }
-
- if (\func_num_args() > 1 && func_get_arg(1)) {
+ if ($normalizeWhitespace) {
return trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $text));
}
@@ -638,11 +608,11 @@ public function text(/* string $default = null, bool $normalizeWhitespace = true
*
* @throws \InvalidArgumentException When current node is empty
*/
- public function html(/* string $default = null */)
+ public function html(string $default = null)
{
if (!$this->nodes) {
- if (0 < \func_num_args() && null !== func_get_arg(0)) {
- return (string) func_get_arg(0);
+ if (null !== $default) {
+ return $default;
}
throw new \InvalidArgumentException('The current node list is empty.');
@@ -685,11 +655,9 @@ public function outerHtml(): string
* Since an XPath expression might evaluate to either a simple type or a \DOMNodeList,
* this method will return either an array of simple types or a new Crawler instance.
*
- * @param string $xpath An XPath expression
- *
* @return array|Crawler An array of evaluation results or a new Crawler instance
*/
- public function evaluate($xpath)
+ public function evaluate(string $xpath)
{
if (null === $this->document) {
throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.');
@@ -718,13 +686,10 @@ public function evaluate($xpath)
*
* $crawler->filter('h1 a')->extract(['_text', 'href']);
*
- * @param array $attributes An array of attributes
- *
* @return array An array of extracted values
*/
- public function extract($attributes)
+ public function extract(array $attributes)
{
- $attributes = (array) $attributes;
$count = \count($attributes);
$data = [];
@@ -754,11 +719,9 @@ public function extract($attributes)
* This means that a child selector "div" or "./div" will match only
* the div elements of the current crawler, not their children.
*
- * @param string $xpath An XPath expression
- *
* @return static
*/
- public function filterXPath($xpath)
+ public function filterXPath(string $xpath)
{
$xpath = $this->relativize($xpath);
@@ -775,13 +738,11 @@ public function filterXPath($xpath)
*
* This method only works if you have installed the CssSelector Symfony Component.
*
- * @param string $selector A CSS selector
- *
* @return static
*
* @throws \RuntimeException if the CssSelector Component is not available
*/
- public function filter($selector)
+ public function filter(string $selector)
{
$converter = $this->createCssSelectorConverter();
@@ -792,11 +753,9 @@ public function filter($selector)
/**
* Selects links by name or alt value for clickable images.
*
- * @param string $value The link text
- *
* @return static
*/
- public function selectLink($value)
+ public function selectLink(string $value)
{
return $this->filterRelativeXPath(
sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %1$s) or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %1$s)]]', static::xpathLiteral(' '.$value.' '))
@@ -806,11 +765,9 @@ public function selectLink($value)
/**
* Selects images by alt value.
*
- * @param string $value The image alt
- *
* @return static A new instance of Crawler with the filtered list of nodes
*/
- public function selectImage($value)
+ public function selectImage(string $value)
{
$xpath = sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value));
@@ -820,11 +777,9 @@ public function selectImage($value)
/**
* Selects a button by name or alt value for images.
*
- * @param string $value The button text
- *
* @return static
*/
- public function selectButton($value)
+ public function selectButton(string $value)
{
return $this->filterRelativeXPath(
sprintf('descendant-or-self::input[((contains(%1$s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %2$s)) or (contains(%1$s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %2$s)) or @id=%3$s or @name=%3$s] | descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %2$s) or @id=%3$s or @name=%3$s]', 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value))
@@ -834,13 +789,11 @@ public function selectButton($value)
/**
* Returns a Link object for the first node in the list.
*
- * @param string $method The method for the link (get by default)
- *
* @return Link A Link instance
*
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement
*/
- public function link($method = 'get')
+ public function link(string $method = 'get')
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -920,14 +873,11 @@ public function images()
/**
* Returns a Form object for the first node in the list.
*
- * @param array $values An array of values for the form fields
- * @param string $method The method for the form
- *
* @return Form A Form instance
*
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement
*/
- public function form(array $values = null, $method = null)
+ public function form(array $values = null, string $method = null)
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -950,19 +900,13 @@ public function form(array $values = null, $method = null)
/**
* Overloads a default namespace prefix to be used with XPath and CSS expressions.
- *
- * @param string $prefix
*/
- public function setDefaultNamespacePrefix($prefix)
+ public function setDefaultNamespacePrefix(string $prefix)
{
$this->defaultNamespacePrefix = $prefix;
}
- /**
- * @param string $prefix
- * @param string $namespace
- */
- public function registerNamespace($prefix, $namespace)
+ public function registerNamespace(string $prefix, string $namespace)
{
$this->namespaces[$prefix] = $namespace;
}
@@ -983,11 +927,9 @@ public function registerNamespace($prefix, $namespace)
* echo Crawler::xpathLiteral('a\'b"c');
* //prints concat('a', "'", 'b"c')
*
- * @param string $s String to be escaped
- *
* @return string Converted string
*/
- public static function xpathLiteral($s)
+ public static function xpathLiteral(string $s)
{
if (false === strpos($s, "'")) {
return sprintf("'%s'", $s);
@@ -1020,7 +962,7 @@ public static function xpathLiteral($s)
*
* @return static
*/
- private function filterRelativeXPath(string $xpath)
+ private function filterRelativeXPath(string $xpath): object
{
$prefixes = $this->findNamespacePrefixes($xpath);
@@ -1125,11 +1067,9 @@ private function relativize(string $xpath): string
}
/**
- * @param int $position
- *
* @return \DOMNode|null
*/
- public function getNode($position)
+ public function getNode(int $position)
{
return isset($this->nodes[$position]) ? $this->nodes[$position] : null;
}
@@ -1152,11 +1092,10 @@ public function getIterator()
/**
* @param \DOMElement $node
- * @param string $siblingDir
*
* @return array
*/
- protected function sibling($node, $siblingDir = 'nextSibling')
+ protected function sibling($node, string $siblingDir = 'nextSibling')
{
$nodes = [];
@@ -1265,7 +1204,7 @@ private function findNamespacePrefixes(string $xpath): array
*
* @return static
*/
- private function createSubCrawler($nodes)
+ private function createSubCrawler($nodes): object
{
$crawler = new static($nodes, $this->uri, $this->baseHref);
$crawler->isHtml = $this->isHtml;
diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php
index 8b437630d089f..c4b08c88b9abb 100644
--- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php
+++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php
@@ -113,7 +113,7 @@ public function untick()
/**
* Sets the value of the field.
*
- * @param string|array|bool $value The value of the field
+ * @param string|array|bool|null $value The value of the field
*
* @throws \InvalidArgumentException When value type provided is not correct
*/
@@ -268,12 +268,9 @@ private function buildOptionValue(\DOMElement $node): array
/**
* Checks whether given value is in the existing options.
*
- * @param string $optionValue
- * @param array $options
- *
* @return bool
*/
- public function containsOption($optionValue, $options)
+ public function containsOption(string $optionValue, array $options)
{
if ($this->validationDisabled) {
return true;
diff --git a/src/Symfony/Component/DomCrawler/Field/FileFormField.php b/src/Symfony/Component/DomCrawler/Field/FileFormField.php
index 9abdca8827ef5..f38aca3f4ee4a 100644
--- a/src/Symfony/Component/DomCrawler/Field/FileFormField.php
+++ b/src/Symfony/Component/DomCrawler/Field/FileFormField.php
@@ -25,7 +25,7 @@ class FileFormField extends FormField
*
* @throws \InvalidArgumentException When error code doesn't exist
*/
- public function setErrorCode($error)
+ public function setErrorCode(int $error)
{
$codes = [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION];
if (!\in_array($error, $codes)) {
@@ -37,20 +37,16 @@ public function setErrorCode($error)
/**
* Sets the value of the field.
- *
- * @param string $value The value of the field
*/
- public function upload($value)
+ public function upload(?string $value)
{
$this->setValue($value);
}
/**
* Sets the value of the field.
- *
- * @param string|null $value The value of the field
*/
- public function setValue($value)
+ public function setValue(?string $value)
{
if (null !== $value && is_readable($value)) {
$error = UPLOAD_ERR_OK;
@@ -80,10 +76,8 @@ public function setValue($value)
/**
* Sets path to the file as string for simulating HTTP request.
- *
- * @param string $path The path to the file
*/
- public function setFilePath($path)
+ public function setFilePath(string $path)
{
parent::setValue($path);
}
diff --git a/src/Symfony/Component/DomCrawler/Field/FormField.php b/src/Symfony/Component/DomCrawler/Field/FormField.php
index 0bc4f54479f3b..3e71edef40da1 100644
--- a/src/Symfony/Component/DomCrawler/Field/FormField.php
+++ b/src/Symfony/Component/DomCrawler/Field/FormField.php
@@ -98,12 +98,10 @@ public function getValue()
/**
* Sets the value of the field.
- *
- * @param string|array|bool|null $value The value of the field
*/
- public function setValue($value)
+ public function setValue(?string $value)
{
- $this->value = (string) $value;
+ $this->value = $value ?? '';
}
/**
diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php
index edde960a381f8..56d10108c1a98 100644
--- a/src/Symfony/Component/DomCrawler/Form.php
+++ b/src/Symfony/Component/DomCrawler/Form.php
@@ -255,21 +255,17 @@ public function getName(): string
/**
* Returns true if the named field exists.
*
- * @param string $name The field name
- *
* @return bool true if the field exists, false otherwise
*/
- public function has($name)
+ public function has(string $name)
{
return $this->fields->has($name);
}
/**
* Removes a field from the form.
- *
- * @param string $name The field name
*/
- public function remove($name)
+ public function remove(string $name)
{
$this->fields->remove($name);
}
@@ -277,13 +273,11 @@ public function remove($name)
/**
* Gets a named field.
*
- * @param string $name The field name
- *
* @return FormField|FormField[]|FormField[][] The value of the field
*
* @throws \InvalidArgumentException When field is not present in this form
*/
- public function get($name)
+ public function get(string $name)
{
return $this->fields->get($name);
}
diff --git a/src/Symfony/Component/DomCrawler/Tests/AbstractCrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/AbstractCrawlerTest.php
index 266b4be6f513f..19c7e678acacd 100644
--- a/src/Symfony/Component/DomCrawler/Tests/AbstractCrawlerTest.php
+++ b/src/Symfony/Component/DomCrawler/Tests/AbstractCrawlerTest.php
@@ -260,15 +260,6 @@ public function testNormalizeWhiteSpace()
$this->assertNotSame('Elsa <3', $crawler->text(null, false));
}
- /**
- * @group legacy
- */
- public function testLegacyNormalizeWhiteSpace()
- {
- $crawler = $this->createTestCrawler()->filterXPath('//p');
- $this->assertNotSame('Elsa <3', $crawler->text());
- }
-
public function testEach()
{
$data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) {
@@ -376,11 +367,11 @@ public function testExtract()
{
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
- $this->assertEquals(['One', 'Two', 'Three'], $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list');
+ $this->assertEquals(['One', 'Two', 'Three'], $crawler->extract(['_text']), '->extract() returns an array of extracted data from the node list');
$this->assertEquals([['One', 'first'], ['Two', ''], ['Three', '']], $crawler->extract(['_text', 'class']), '->extract() returns an array of extracted data from the node list');
$this->assertEquals([[], [], []], $crawler->extract([]), '->extract() returns empty arrays if the attribute list is empty');
- $this->assertEquals([], $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty');
+ $this->assertEquals([], $this->createTestCrawler()->filterXPath('//ol')->extract(['_text']), '->extract() returns an empty array if the node list is empty');
$this->assertEquals([['One', 'li'], ['Two', 'li'], ['Three', 'li']], $crawler->extract(['_text', '_name']), '->extract() returns an array of extracted data from the node list');
}
@@ -1203,62 +1194,6 @@ public function testEvaluateThrowsAnExceptionIfDocumentIsEmpty()
$this->createCrawler()->evaluate('//form/input[1]');
}
- /**
- * @group legacy
- * @expectedDeprecation The "Symfony\Component\DomCrawler\Crawler::children()" method will have a new "string $selector = null" argument in version 5.0, not defining it is deprecated since Symfony 4.2.
- */
- public function testInheritedClassCallChildrenWithoutArgument()
- {
- $dom = new \DOMDocument();
- $dom->loadHTML($this->getDoctype().'
-
-
- Foo
- Fabien\'s Foo
- Fabien"s Foo
- \' Fabien"s Foo
-
-
-
-
-
-
- GetLink
-
- Klausi|Claudiu
-
-
-
-
-
-
-
-
- - One Bis
- - Two Bis
- - Three Bis
-
-
- ![]()
-
-
- ');
- $crawlerChild = new ClassThatInheritCrawler($dom);
- $crawlerChild->children();
- }
-
public function testAddHtmlContentUnsupportedCharset()
{
$crawler = $this->createCrawler();
diff --git a/src/Symfony/Component/DomCrawler/Tests/ClassThatInheritCrawler.php b/src/Symfony/Component/DomCrawler/Tests/ClassThatInheritCrawler.php
deleted file mode 100644
index 2b2ac1092ed23..0000000000000
--- a/src/Symfony/Component/DomCrawler/Tests/ClassThatInheritCrawler.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DomCrawler\Tests;
-
-use Symfony\Component\DomCrawler\Crawler;
-
-class ClassThatInheritCrawler extends Crawler
-{
- /**
- * @return static
- */
- public function children()
- {
- return parent::children();
- }
-}
diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php
index b14bcc855e2ab..381386cd813e3 100644
--- a/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php
+++ b/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php
@@ -96,7 +96,7 @@ public function testSetErrorCode()
$this->assertEquals(UPLOAD_ERR_FORM_SIZE, $value['error'], '->setErrorCode() sets the file input field error code');
try {
- $field->setErrorCode('foobar');
+ $field->setErrorCode(12345);
$this->fail('->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
} catch (\InvalidArgumentException $e) {
$this->assertTrue(true, '->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
diff --git a/src/Symfony/Component/DomCrawler/Tests/UriResolverTest.php b/src/Symfony/Component/DomCrawler/Tests/UriResolverTest.php
new file mode 100644
index 0000000000000..c62d7d3811338
--- /dev/null
+++ b/src/Symfony/Component/DomCrawler/Tests/UriResolverTest.php
@@ -0,0 +1,86 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DomCrawler\UriResolver;
+
+class UriResolverTest extends TestCase
+{
+ /**
+ * @dataProvider provideResolverTests
+ */
+ public function testResolver(string $uri, string $baseUri, string $expected)
+ {
+ $this->assertEquals($expected, UriResolver::resolve($uri, $baseUri));
+ }
+
+ public function provideResolverTests()
+ {
+ return [
+ ['/foo', 'http://localhost/bar/foo/', 'http://localhost/foo'],
+ ['/foo', 'http://localhost/bar/foo', 'http://localhost/foo'],
+ ['
+ /foo', 'http://localhost/bar/foo/', 'http://localhost/foo'],
+ ['/foo
+ ', 'http://localhost/bar/foo', 'http://localhost/foo'],
+
+ ['foo', 'http://localhost/bar/foo/', 'http://localhost/bar/foo/foo'],
+ ['foo', 'http://localhost/bar/foo', 'http://localhost/bar/foo'],
+
+ ['', 'http://localhost/bar/', 'http://localhost/bar/'],
+ ['#', 'http://localhost/bar/', 'http://localhost/bar/#'],
+ ['#bar', 'http://localhost/bar?a=b', 'http://localhost/bar?a=b#bar'],
+ ['#bar', 'http://localhost/bar/#foo', 'http://localhost/bar/#bar'],
+ ['?a=b', 'http://localhost/bar#foo', 'http://localhost/bar?a=b'],
+ ['?a=b', 'http://localhost/bar/', 'http://localhost/bar/?a=b'],
+
+ ['http://login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'],
+ ['https://login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'],
+ ['mailto:foo@bar.com', 'http://localhost/foo', 'mailto:foo@bar.com'],
+
+ // tests schema relative URL (issue #7169)
+ ['//login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'],
+ ['//login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'],
+
+ ['?foo=2', 'http://localhost?foo=1', 'http://localhost?foo=2'],
+ ['?foo=2', 'http://localhost/?foo=1', 'http://localhost/?foo=2'],
+ ['?foo=2', 'http://localhost/bar?foo=1', 'http://localhost/bar?foo=2'],
+ ['?foo=2', 'http://localhost/bar/?foo=1', 'http://localhost/bar/?foo=2'],
+ ['?bar=2', 'http://localhost?foo=1', 'http://localhost?bar=2'],
+
+ ['foo', 'http://login.foo.com/bar/baz?/query/string', 'http://login.foo.com/bar/foo'],
+
+ ['.', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'],
+ ['./', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'],
+ ['./foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/foo'],
+ ['..', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'],
+ ['../', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'],
+ ['../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/foo'],
+ ['../..', 'http://localhost/foo/bar/baz', 'http://localhost/'],
+ ['../../', 'http://localhost/foo/bar/baz', 'http://localhost/'],
+ ['../../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo'],
+ ['../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'],
+ ['../bar/../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'],
+ ['../bar/./../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'],
+ ['../../', 'http://localhost/', 'http://localhost/'],
+ ['../../', 'http://localhost', 'http://localhost/'],
+
+ ['/foo', 'http://localhost?bar=1', 'http://localhost/foo'],
+ ['/foo', 'http://localhost#bar', 'http://localhost/foo'],
+ ['/foo', 'file:///', 'file:///foo'],
+ ['/foo', 'file:///bar/baz', 'file:///foo'],
+ ['foo', 'file:///', 'file:///foo'],
+ ['foo', 'file:///bar/baz', 'file:///bar/foo'],
+ ];
+ }
+}
diff --git a/src/Symfony/Component/DomCrawler/UriResolver.php b/src/Symfony/Component/DomCrawler/UriResolver.php
new file mode 100644
index 0000000000000..5a57fcc51739d
--- /dev/null
+++ b/src/Symfony/Component/DomCrawler/UriResolver.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+/**
+ * The UriResolver class takes an URI (relative, absolute, fragment, etc.)
+ * and turns it into an absolute URI against another given base URI.
+ *
+ * @author Fabien Potencier
+ * @author Grégoire Pineau
+ */
+class UriResolver
+{
+ /**
+ * Resolves a URI according to a base URI.
+ *
+ * For example if $uri=/foo/bar and $baseUri=https://symfony.com it will
+ * return https://symfony.com/foo/bar
+ *
+ * If the $uri is not absolute you must pass an absolute $baseUri
+ */
+ public static function resolve(string $uri, ?string $baseUri): string
+ {
+ $uri = trim($uri);
+
+ // absolute URL?
+ if (null !== parse_url($uri, PHP_URL_SCHEME)) {
+ return $uri;
+ }
+
+ if (null === $baseUri) {
+ throw new \InvalidArgumentException('The URI is relative, so you must define its base URI passing an absolute URL.');
+ }
+
+ // empty URI
+ if (!$uri) {
+ return $baseUri;
+ }
+
+ // an anchor
+ if ('#' === $uri[0]) {
+ return self::cleanupAnchor($baseUri).$uri;
+ }
+
+ $baseUriCleaned = self::cleanupUri($baseUri);
+
+ if ('?' === $uri[0]) {
+ return $baseUriCleaned.$uri;
+ }
+
+ // absolute URL with relative schema
+ if (0 === strpos($uri, '//')) {
+ return preg_replace('#^([^/]*)//.*$#', '$1', $baseUriCleaned).$uri;
+ }
+
+ $baseUriCleaned = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUriCleaned);
+
+ // absolute path
+ if ('/' === $uri[0]) {
+ return $baseUriCleaned.$uri;
+ }
+
+ // relative path
+ $path = parse_url(substr($baseUri, \strlen($baseUriCleaned)), PHP_URL_PATH);
+ $path = self::canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
+
+ return $baseUriCleaned.('' === $path || '/' !== $path[0] ? '/' : '').$path;
+ }
+
+ /**
+ * Returns the canonicalized URI path (see RFC 3986, section 5.2.4).
+ */
+ private static function canonicalizePath(string $path): string
+ {
+ if ('' === $path || '/' === $path) {
+ return $path;
+ }
+
+ if ('.' === substr($path, -1)) {
+ $path .= '/';
+ }
+
+ $output = [];
+
+ foreach (explode('/', $path) as $segment) {
+ if ('..' === $segment) {
+ array_pop($output);
+ } elseif ('.' !== $segment) {
+ $output[] = $segment;
+ }
+ }
+
+ return implode('/', $output);
+ }
+
+ /**
+ * Removes the query string and the anchor from the given uri.
+ */
+ private static function cleanupUri(string $uri): string
+ {
+ return self::cleanupQuery(self::cleanupAnchor($uri));
+ }
+
+ /**
+ * Removes the query string from the uri.
+ */
+ private static function cleanupQuery(string $uri): string
+ {
+ if (false !== $pos = strpos($uri, '?')) {
+ return substr($uri, 0, $pos);
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Removes the anchor from the uri.
+ */
+ private static function cleanupAnchor(string $uri): string
+ {
+ if (false !== $pos = strpos($uri, '#')) {
+ return substr($uri, 0, $pos);
+ }
+
+ return $uri;
+ }
+}
diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json
index 81ddb8cbb8fb6..ae5cf5ca05c97 100644
--- a/src/Symfony/Component/DomCrawler/composer.json
+++ b/src/Symfony/Component/DomCrawler/composer.json
@@ -16,12 +16,12 @@
}
],
"require": {
- "php": "^7.1.3",
+ "php": "^7.2.5",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
- "symfony/css-selector": "^3.4|^4.0|^5.0",
+ "symfony/css-selector": "^4.4|^5.0",
"masterminds/html5": "^2.6"
},
"conflict": {
@@ -39,7 +39,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Dotenv/CHANGELOG.md b/src/Symfony/Component/Dotenv/CHANGELOG.md
index e6e74c945bb40..e004ed8d75d67 100644
--- a/src/Symfony/Component/Dotenv/CHANGELOG.md
+++ b/src/Symfony/Component/Dotenv/CHANGELOG.md
@@ -1,6 +1,20 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * added `Dotenv::bootEnv()` to check for `.env.local.php` before calling `Dotenv::loadEnv()`
+ * added `Dotenv::setProdEnvs()` and `Dotenv::usePutenv()`
+ * made Dotenv's constructor accept `$envKey` and `$debugKey` arguments, to define
+ the name of the env vars that configure the env name and debug settings
+ * deprecated passing `$usePutenv` argument to Dotenv's constructor
+
+5.0.0
+-----
+
+ * using `putenv()` is disabled by default
+
4.3.0
-----
diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php
index a0154c400c6e9..1b4966ccc1806 100644
--- a/src/Symfony/Component/Dotenv/Dotenv.php
+++ b/src/Symfony/Component/Dotenv/Dotenv.php
@@ -35,20 +35,47 @@ final class Dotenv
private $data;
private $end;
private $values;
- private $usePutenv;
+ private $envKey;
+ private $debugKey;
+ private $prodEnvs = ['prod'];
+ private $usePutenv = false;
/**
- * @var bool If `putenv()` should be used to define environment variables or not.
- * Beware that `putenv()` is not thread safe and this setting will default
- * to `false` in Symfony 5.0.
+ * @param string $envKey
*/
- public function __construct(bool $usePutenv = true)
+ public function __construct($envKey = 'APP_ENV', string $debugKey = 'APP_DEBUG')
{
- if (!\func_num_args()) {
- @trigger_error(sprintf('The default value of "$usePutenv" argument of "%s" will be changed from "true" to "false" in Symfony 5.0. You should define its value explicitly.', __METHOD__), E_USER_DEPRECATED);
+ if (\in_array($envKey = (string) $envKey, ['1', ''], true)) {
+ trigger_deprecation('symfony/dotenv', '5.1', 'Passing a boolean to the constructor of "%s" is deprecated, use "Dotenv::usePutenv()".', __CLASS__);
+ $this->usePutenv = (bool) $envKey;
+ $envKey = 'APP_ENV';
}
+ $this->envKey = $envKey;
+ $this->debugKey = $debugKey;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setProdEnvs(array $prodEnvs): self
+ {
+ $this->prodEnvs = $prodEnvs;
+
+ return $this;
+ }
+
+ /**
+ * @param bool $usePutenv If `putenv()` should be used to define environment variables or not.
+ * Beware that `putenv()` is not thread safe, that's why this setting defaults to false
+ *
+ * @return $this
+ */
+ public function usePutenv($usePutenv = true): self
+ {
$this->usePutenv = $usePutenv;
+
+ return $this;
}
/**
@@ -71,29 +98,31 @@ public function load(string $path, string ...$extraPaths): void
* .env.local is always ignored in test env because tests should produce the same results for everyone.
* .env.dist is loaded when it exists and .env is not found.
*
- * @param string $path A file to load
- * @param string $varName The name of the env vars that defines the app env
- * @param string $defaultEnv The app env to use when none is defined
- * @param array $testEnvs A list of app envs for which .env.local should be ignored
+ * @param string $path A file to load
+ * @param string $envKey|null The name of the env vars that defines the app env
+ * @param string $defaultEnv The app env to use when none is defined
+ * @param array $testEnvs A list of app envs for which .env.local should be ignored
*
* @throws FormatException when a file has a syntax error
* @throws PathException when a file does not exist or is not readable
*/
- public function loadEnv(string $path, string $varName = 'APP_ENV', string $defaultEnv = 'dev', array $testEnvs = ['test']): void
+ public function loadEnv(string $path, string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test']): void
{
+ $k = $envKey ?? $this->envKey;
+
if (file_exists($path) || !file_exists($p = "$path.dist")) {
$this->load($path);
} else {
$this->load($p);
}
- if (null === $env = $_SERVER[$varName] ?? $_ENV[$varName] ?? null) {
- $this->populate([$varName => $env = $defaultEnv]);
+ if (null === $env = $_SERVER[$k] ?? $_ENV[$k] ?? null) {
+ $this->populate([$k => $env = $defaultEnv]);
}
if (!\in_array($env, $testEnvs, true) && file_exists($p = "$path.local")) {
$this->load($p);
- $env = $_SERVER[$varName] ?? $_ENV[$varName] ?? $env;
+ $env = $_SERVER[$k] ?? $_ENV[$k] ?? $env;
}
if ('local' === $env) {
@@ -109,6 +138,32 @@ public function loadEnv(string $path, string $varName = 'APP_ENV', string $defau
}
}
+ /**
+ * Loads env vars from .env.local.php if the file exists or from the other .env files otherwise.
+ *
+ * This method also configures the APP_DEBUG env var according to the current APP_ENV.
+ *
+ * See method loadEnv() for rules related to .env files.
+ */
+ public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnvs = ['test']): void
+ {
+ $p = $path.'.local.php';
+ $env = (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($p)) || file_exists($p) ? include $p : null;
+ $k = $this->envKey;
+
+ if (\is_array($env) && (!isset($env[$k]) || ($_SERVER[$k] ?? $_ENV[$k] ?? $env[$k]) === $env[$k])) {
+ $this->populate($env);
+ } else {
+ $this->loadEnv($path, $k, $defaultEnv, $testEnvs);
+ }
+
+ $_SERVER += $_ENV;
+
+ $k = $this->debugKey;
+ $debug = $_SERVER[$k] ?? !\in_array($_SERVER[$this->envKey], $this->prodEnvs, true);
+ $_SERVER[$k] = $_ENV[$k] = (int) $debug || (!\is_bool($debug) && filter_var($debug, FILTER_VALIDATE_BOOLEAN)) ? '1' : '0';
+ }
+
/**
* Loads one or several .env files and enables override existing vars.
*
diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php
index 9cf3b66e2c393..f43ebac6ace98 100644
--- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php
+++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php
@@ -22,7 +22,7 @@ class DotenvTest extends TestCase
*/
public function testParseWithFormatError($data, $error)
{
- $dotenv = new Dotenv(true);
+ $dotenv = new Dotenv();
try {
$dotenv->parse($data);
@@ -66,7 +66,7 @@ public function getEnvDataWithFormatErrors()
*/
public function testParse($data, $expected)
{
- $dotenv = new Dotenv(true);
+ $dotenv = new Dotenv();
$this->assertSame($expected, $dotenv->parse($data));
}
@@ -208,7 +208,7 @@ public function testLoad()
file_put_contents($path1, 'FOO=BAR');
file_put_contents($path2, 'BAR=BAZ');
- (new Dotenv(true))->load($path1, $path2);
+ (new Dotenv())->usePutenv()->load($path1, $path2);
$foo = getenv('FOO');
$bar = getenv('BAR');
@@ -239,7 +239,7 @@ public function testLoadEnv()
// .env
file_put_contents($path, 'FOO=BAR');
- (new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
+ (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('BAR', getenv('FOO'));
$this->assertSame('dev', getenv('TEST_APP_ENV'));
@@ -247,33 +247,33 @@ public function testLoadEnv()
$_SERVER['TEST_APP_ENV'] = 'local';
file_put_contents("$path.local", 'FOO=localBAR');
- (new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
+ (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('localBAR', getenv('FOO'));
// special case for test
$_SERVER['TEST_APP_ENV'] = 'test';
- (new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
+ (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('BAR', getenv('FOO'));
// .env.dev
unset($_SERVER['TEST_APP_ENV']);
file_put_contents("$path.dev", 'FOO=devBAR');
- (new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
+ (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('devBAR', getenv('FOO'));
// .env.dev.local
file_put_contents("$path.dev.local", 'FOO=devlocalBAR');
- (new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
+ (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('devlocalBAR', getenv('FOO'));
// .env.dist
unlink($path);
file_put_contents("$path.dist", 'BAR=distBAR');
- (new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
+ (new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('distBAR', getenv('BAR'));
putenv('FOO');
@@ -305,7 +305,7 @@ public function testOverload()
file_put_contents($path1, 'FOO=BAR');
file_put_contents($path2, 'BAR=BAZ');
- (new Dotenv(true))->overload($path1, $path2);
+ (new Dotenv())->usePutenv()->overload($path1, $path2);
$foo = getenv('FOO');
$bar = getenv('BAR');
@@ -323,7 +323,7 @@ public function testOverload()
public function testLoadDirectory()
{
$this->expectException('Symfony\Component\Dotenv\Exception\PathException');
- $dotenv = new Dotenv(true);
+ $dotenv = new Dotenv();
$dotenv->load(__DIR__);
}
@@ -331,7 +331,7 @@ public function testServerSuperglobalIsNotOverridden()
{
$originalValue = $_SERVER['argc'];
- $dotenv = new Dotenv(true);
+ $dotenv = new Dotenv();
$dotenv->populate(['argc' => 'new_value']);
$this->assertSame($originalValue, $_SERVER['argc']);
@@ -342,7 +342,7 @@ public function testEnvVarIsNotOverridden()
putenv('TEST_ENV_VAR=original_value');
$_SERVER['TEST_ENV_VAR'] = 'original_value';
- $dotenv = new Dotenv(true);
+ $dotenv = (new Dotenv())->usePutenv();
$dotenv->populate(['TEST_ENV_VAR' => 'new_value']);
$this->assertSame('original_value', getenv('TEST_ENV_VAR'));
@@ -352,7 +352,7 @@ public function testHttpVarIsPartiallyOverridden()
{
$_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value';
- $dotenv = new Dotenv(true);
+ $dotenv = (new Dotenv())->usePutenv();
$dotenv->populate(['HTTP_TEST_ENV_VAR' => 'env_value']);
$this->assertSame('env_value', getenv('HTTP_TEST_ENV_VAR'));
@@ -364,7 +364,7 @@ public function testEnvVarIsOverriden()
{
putenv('TEST_ENV_VAR_OVERRIDEN=original_value');
- $dotenv = new Dotenv(true);
+ $dotenv = (new Dotenv())->usePutenv();
$dotenv->populate(['TEST_ENV_VAR_OVERRIDEN' => 'new_value'], true);
$this->assertSame('new_value', getenv('TEST_ENV_VAR_OVERRIDEN'));
@@ -386,7 +386,7 @@ public function testMemorizingLoadedVarsNamesInSpecialVar()
unset($_SERVER['DATABASE_URL']);
putenv('DATABASE_URL');
- $dotenv = new Dotenv(true);
+ $dotenv = (new Dotenv())->usePutenv();
$dotenv->populate(['APP_DEBUG' => '1', 'DATABASE_URL' => 'mysql://root@localhost/db']);
$this->assertSame('APP_DEBUG,DATABASE_URL', getenv('SYMFONY_DOTENV_VARS'));
@@ -403,7 +403,7 @@ public function testMemorizingLoadedVarsNamesInSpecialVar()
unset($_SERVER['DATABASE_URL']);
putenv('DATABASE_URL');
- $dotenv = new Dotenv(true);
+ $dotenv = (new Dotenv())->usePutenv();
$dotenv->populate(['APP_DEBUG' => '0', 'DATABASE_URL' => 'mysql://root@localhost/db']);
$dotenv->populate(['DATABASE_URL' => 'sqlite:///somedb.sqlite']);
@@ -419,7 +419,7 @@ public function testOverridingEnvVarsWithNamesMemorizedInSpecialVar()
putenv('BAZ=baz');
putenv('DOCUMENT_ROOT=/var/www');
- $dotenv = new Dotenv(true);
+ $dotenv = (new Dotenv())->usePutenv();
$dotenv->populate(['FOO' => 'foo1', 'BAR' => 'bar1', 'BAZ' => 'baz1', 'DOCUMENT_ROOT' => '/boot']);
$this->assertSame('foo1', getenv('FOO'));
@@ -431,7 +431,7 @@ public function testOverridingEnvVarsWithNamesMemorizedInSpecialVar()
public function testGetVariablesValueFromEnvFirst()
{
$_ENV['APP_ENV'] = 'prod';
- $dotenv = new Dotenv(true);
+ $dotenv = new Dotenv();
$test = "APP_ENV=dev\nTEST1=foo1_\${APP_ENV}";
$values = $dotenv->parse($test);
@@ -448,7 +448,7 @@ public function testGetVariablesValueFromGetenv()
{
putenv('Foo=Bar');
- $dotenv = new Dotenv(true);
+ $dotenv = new Dotenv();
try {
$values = $dotenv->parse('Foo=${Foo}');
@@ -458,30 +458,42 @@ public function testGetVariablesValueFromGetenv()
}
}
- /**
- * @group legacy
- * @expectedDeprecation The default value of "$usePutenv" argument of "%s" will be changed from "true" to "false" in Symfony 5.0. You should define its value explicitly.
- */
- public function testDeprecationWarning()
- {
- new Dotenv();
- }
-
public function testNoDeprecationWarning()
{
- $dotenv = new Dotenv(true);
- $this->assertInstanceOf(Dotenv::class, $dotenv);
- $dotenv = new Dotenv(false);
+ $dotenv = new Dotenv();
$this->assertInstanceOf(Dotenv::class, $dotenv);
}
public function testDoNotUsePutenv()
{
- $dotenv = new Dotenv(false);
+ $dotenv = new Dotenv();
$dotenv->populate(['TEST_USE_PUTENV' => 'no']);
$this->assertSame('no', $_SERVER['TEST_USE_PUTENV']);
$this->assertSame('no', $_ENV['TEST_USE_PUTENV']);
$this->assertFalse(getenv('TEST_USE_PUTENV'));
}
+
+ public function testBootEnv()
+ {
+ @mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
+ $path = tempnam($tmpdir, 'sf-');
+
+ file_put_contents($path, 'FOO=BAR');
+ (new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path);
+
+ $this->assertSame('BAR', $_SERVER['FOO']);
+
+ unset($_SERVER['FOO'], $_ENV['FOO']);
+ unlink($path);
+
+ file_put_contents($path.'.local.php', ' "dev", "FOO" => "BAR"];');
+ (new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path);
+ $this->assertSame('BAR', $_SERVER['FOO']);
+ $this->assertSame('1', $_SERVER['TEST_APP_DEBUG']);
+
+ unset($_SERVER['FOO'], $_ENV['FOO']);
+ unlink($path.'.local.php');
+ rmdir($tmpdir);
+ }
}
diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json
index 5520f45397794..869620993b457 100644
--- a/src/Symfony/Component/Dotenv/composer.json
+++ b/src/Symfony/Component/Dotenv/composer.json
@@ -16,10 +16,11 @@
}
],
"require": {
- "php": "^7.1.3"
+ "php": "^7.2.5",
+ "symfony/deprecation-contracts": "^2.1"
},
"require-dev": {
- "symfony/process": "^3.4.2|^4.0|^5.0"
+ "symfony/process": "^4.4|^5.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Dotenv\\": "" },
@@ -30,7 +31,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php
index 71f21f50fadf0..523c2ef786aff 100644
--- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php
+++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php
@@ -401,13 +401,12 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
if (
'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7' === $class
|| 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV6' === $class
- || 'Test\Symfony\Component\Debug\Tests' === $refl->getNamespaceName()
) {
return [];
}
$deprecations = [];
- $className = isset($class[15]) && "\0" === $class[15] && 0 === strpos($class, "class@anonymous\x00") ? get_parent_class($class).'@anonymous' : $class;
+ $className = isset($class[15]) && "\0" === $class[15] && 0 === strpos($class, "class@anonymous\x00") ? (get_parent_class($class) ?: key(class_implements($class))).'@anonymous' : $class;
// Don't trigger deprecations for classes in the same vendor
if ($class !== $className) {
@@ -757,7 +756,7 @@ private function darwinRealpath(string $real): string
}
if (isset($dirFiles[$file])) {
- return $real .= $dirFiles[$file];
+ return $real.$dirFiles[$file];
}
$kFile = strtolower($file);
@@ -776,7 +775,7 @@ private function darwinRealpath(string $real): string
self::$darwinCache[$kDir][1] = $dirFiles;
}
- return $real .= $dirFiles[$kFile];
+ return $real.$dirFiles[$kFile];
}
/**
diff --git a/src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php b/src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php
index 38111078bc311..7cd2b382f1636 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php
@@ -11,8 +11,7 @@
namespace Symfony\Component\ErrorHandler\ErrorEnhancer;
-use Composer\Autoload\ClassLoader as ComposerClassLoader;
-use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
+use Composer\Autoload\ClassLoader;
use Symfony\Component\ErrorHandler\DebugClassLoader;
use Symfony\Component\ErrorHandler\Error\ClassNotFoundError;
use Symfony\Component\ErrorHandler\Error\FatalError;
@@ -107,14 +106,13 @@ private function getClassCandidates(string $class): array
}
}
- if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
+ if ($function[0] instanceof ClassLoader) {
foreach ($function[0]->getPrefixes() as $prefix => $paths) {
foreach ($paths as $path) {
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
}
}
- }
- if ($function[0] instanceof ComposerClassLoader) {
+
foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) {
foreach ($paths as $path) {
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
index 2ac16e380d032..a5ba6754c6a4a 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
@@ -769,7 +769,7 @@ private function cleanTrace(array $backtrace, int $type, string $file, int $line
private function parseAnonymousClass(string $message): string
{
return preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', static function ($m) {
- return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
+ return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0]))).'@anonymous' : $m[0];
}, $message);
}
}
diff --git a/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php b/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php
index 07c3d49db05d1..331bfd2f53422 100644
--- a/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php
+++ b/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php
@@ -11,8 +11,6 @@
namespace Symfony\Component\ErrorHandler\Exception;
-use Symfony\Component\Debug\Exception\FatalThrowableError;
-use Symfony\Component\Debug\Exception\FlattenException as LegacyFlattenException;
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
@@ -24,7 +22,7 @@
*
* @author Fabien Potencier
*/
-class FlattenException extends LegacyFlattenException
+class FlattenException
{
private $message;
private $code;
@@ -39,11 +37,17 @@ class FlattenException extends LegacyFlattenException
private $line;
private $asString;
+ /**
+ * @return static
+ */
public static function create(\Exception $exception, $statusCode = null, array $headers = []): self
{
return static::createFromThrowable($exception, $statusCode, $headers);
}
+ /**
+ * @return static
+ */
public static function createFromThrowable(\Throwable $exception, int $statusCode = null, array $headers = []): self
{
$e = new static();
@@ -71,7 +75,7 @@ public static function createFromThrowable(\Throwable $exception, int $statusCod
$e->setStatusCode($statusCode);
$e->setHeaders($headers);
$e->setTraceFromThrowable($exception);
- $e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception));
+ $e->setClass(\get_class($exception));
$e->setFile($exception->getFile());
$e->setLine($exception->getLine());
@@ -138,7 +142,7 @@ public function getClass(): string
*/
public function setClass($class): self
{
- $this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
+ $this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class))).'@anonymous' : $class;
return $this;
}
@@ -197,7 +201,7 @@ public function setMessage($message): self
{
if (false !== strpos($message, "class@anonymous\0")) {
$message = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
- return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
+ return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0]))).'@anonymous' : $m[0];
}, $message);
}
@@ -221,10 +225,7 @@ public function setCode($code): self
return $this;
}
- /**
- * @return self|null
- */
- public function getPrevious()
+ public function getPrevious(): ?self
{
return $this->previous;
}
@@ -232,7 +233,7 @@ public function getPrevious()
/**
* @return $this
*/
- final public function setPrevious(LegacyFlattenException $previous): self
+ public function setPrevious(self $previous): self
{
$this->previous = $previous;
@@ -258,16 +259,6 @@ public function getTrace(): array
return $this->trace;
}
- /**
- * @deprecated since 4.1, use {@see setTraceFromThrowable()} instead.
- */
- public function setTraceFromException(\Exception $exception)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED);
-
- $this->setTraceFromThrowable($exception);
- }
-
/**
* @return $this
*/
diff --git a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css
index 952c66d2fc936..e873c7366f84d 100644
--- a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css
+++ b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css
@@ -42,13 +42,54 @@
--base-6: #222;
}
+.theme-dark {
+ --page-background: #36393e;
+ --color-text: #e0e0e0;
+ --color-muted: #777;
+ --color-error: #d43934;
+ --tab-background: #555;
+ --tab-color: #ccc;
+ --tab-active-background: #888;
+ --tab-active-color: #fafafa;
+ --tab-disabled-background: var(--page-background);
+ --tab-disabled-color: #777;
+ --metric-value-background: #555;
+ --metric-value-color: inherit;
+ --metric-unit-color: #999;
+ --metric-label-background: #777;
+ --metric-label-color: #e0e0e0;
+ --trace-selected-background: #71663acc;
+ --table-border: #444;
+ --table-background: #333;
+ --table-header: #555;
+ --info-background: rgba(79, 148, 195, 0.5);
+ --tree-active-background: var(--metric-label-background);
+ --exception-title-color: var(--base-2);
+ --shadow: 0px 0px 1px rgba(32, 32, 32, .2);
+ --border: 1px solid #666;
+ --background-error: #b0413e;
+ --highlight-comment: #dedede;
+ --highlight-default: var(--base-6);
+ --highlight-keyword: #ff413c;
+ --highlight-string: #70a6fd;
+ --base-0: #2e3136;
+ --base-1: #444;
+ --base-2: #666;
+ --base-3: #666;
+ --base-4: #666;
+ --base-5: #e0e0e0;
+ --base-6: #f5f5f5;
+ --card-label-background: var(--tab-active-background);
+ --card-label-color: var(--tab-active-color);
+}
+
html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}
html {
/* always display the vertical scrollbar to avoid jumps when toggling contents */
overflow-y: scroll;
}
-body { background-color: #F9F9F9; color: var(--base-6); font: 14px/1.4 Helvetica, Arial, sans-serif; padding-bottom: 45px; }
+body { background-color: var(--page-background); color: var(--base-6); font: 14px/1.4 Helvetica, Arial, sans-serif; padding-bottom: 45px; }
a { cursor: pointer; text-decoration: none; }
a:hover { text-decoration: underline; }
@@ -56,8 +97,8 @@ abbr[title] { border-bottom: none; cursor: help; text-decoration: none; }
code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; }
-table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; }
-table { background: #FFF; border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
+table, tr, th, td { background: var(--base-0); border-collapse: collapse; vertical-align: top; }
+table { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
table th, table td { border: solid var(--base-2); border-width: 1px 0; padding: 8px 10px; }
table th { background-color: var(--base-2); font-weight: bold; text-align: left; }
@@ -79,7 +120,7 @@ table th { background-color: var(--base-2); font-weight: bold; text-align: left;
.status-warning { background: rgba(240, 181, 24, 0.3); }
.status-error { background: rgba(176, 65, 62, 0.2); }
.status-success td, .status-warning td, .status-error td { background: transparent; }
-tr.status-error td, tr.status-warning td { border-bottom: 1px solid #FAFAFA; border-top: 1px solid #FAFAFA; }
+tr.status-error td, tr.status-warning td { border-bottom: 1px solid var(--base-2); border-top: 1px solid var(--base-2); }
.status-warning .colored { color: #A46A1F; }
.status-error .colored { color: var(--color-error); }
@@ -139,7 +180,7 @@ thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-vis
.container { max-width: 1024px; margin: 0 auto; padding: 0 15px; }
.container::after { content: ""; display: table; clear: both; }
-header { background-color: var(--base-6); color: rgba(255, 255, 255, 0.75); font-size: 13px; height: 33px; line-height: 33px; padding: 0; }
+header { background-color: #222; color: rgba(255, 255, 255, 0.75); font-size: 13px; height: 33px; line-height: 33px; padding: 0; }
header .container { display: flex; justify-content: space-between; }
.logo { flex: 1; font-size: 13px; font-weight: normal; margin: 0; padding: 0; }
.logo svg { height: 18px; width: 18px; opacity: .8; vertical-align: -5px; }
@@ -174,7 +215,7 @@ header .container { display: flex; justify-content: space-between; }
.trace-head .trace-class { color: var(--base-6); font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }
.trace-head .trace-namespace { color: #999; display: block; font-size: 13px; }
.trace-head .icon { position: absolute; right: 0; top: 0; }
-.trace-head .icon svg { height: 24px; width: 24px; }
+.trace-head .icon svg { fill: var(--base-5); height: 24px; width: 24px; }
.trace-details { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; table-layout: fixed; }
@@ -185,7 +226,7 @@ header .container { display: flex; justify-content: space-between; }
.trace-line:hover { background: var(--base-1); }
.trace-line a { color: var(--base-6); }
.trace-line .icon { opacity: .4; position: absolute; left: 10px; top: 11px; }
-.trace-line .icon svg { height: 16px; width: 16px; }
+.trace-line .icon svg { fill: var(--base-5); height: 16px; width: 16px; }
.trace-line-header { padding-left: 36px; padding-right: 10px; }
.trace-file-path, .trace-file-path a { color: var(--base-6); font-size: 13px; }
diff --git a/src/Symfony/Component/ErrorHandler/Resources/views/exception_full.html.php b/src/Symfony/Component/ErrorHandler/Resources/views/exception_full.html.php
index 4d46d59de5ff0..80b813e62f143 100644
--- a/src/Symfony/Component/ErrorHandler/Resources/views/exception_full.html.php
+++ b/src/Symfony/Component/ErrorHandler/Resources/views/exception_full.html.php
@@ -11,6 +11,12 @@
+
+
diff --git a/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php b/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php
index 66212a81ad84d..5783de38bcc39 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\ErrorHandler\Tests\Exception;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -130,19 +129,6 @@ public function testFlattenHttpException(\Throwable $exception)
$this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception');
}
- /**
- * @group legacy
- */
- public function testWrappedThrowable()
- {
- $exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42));
- $flattened = FlattenException::create($exception);
-
- $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.');
- $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.');
- $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error');
- }
-
public function testThrowable()
{
$error = new \DivisionByZeroError('Ouch', 42);
@@ -301,7 +287,7 @@ function () {},
// assertEquals() does not like NAN values.
$this->assertEquals($array[$i][0], 'float');
- $this->assertNan($array[$i++][1]);
+ $this->assertNan($array[$i][1]);
}
public function testRecursionInArguments()
diff --git a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/VirtualClassMagicCall.php b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/VirtualClassMagicCall.php
index 77059e3e72c4e..405b553562450 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/VirtualClassMagicCall.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/VirtualClassMagicCall.php
@@ -8,11 +8,11 @@
*/
class VirtualClassMagicCall
{
- public static function __callStatic($name, $arguments)
+ public static function __callStatic(string $name, array $arguments)
{
}
- public function __call($name, $arguments)
+ public function __call(string $name, array $arguments)
{
}
}
diff --git a/src/Symfony/Component/ErrorHandler/composer.json b/src/Symfony/Component/ErrorHandler/composer.json
index 614bd4f5ac1f3..e36794b065c8f 100644
--- a/src/Symfony/Component/ErrorHandler/composer.json
+++ b/src/Symfony/Component/ErrorHandler/composer.json
@@ -16,9 +16,8 @@
}
],
"require": {
- "php": "^7.1.3",
- "psr/log": "~1.0",
- "symfony/debug": "^4.4.5",
+ "php": "^7.2.5",
+ "psr/log": "^1.0",
"symfony/var-dumper": "^4.4|^5.0"
},
"require-dev": {
@@ -34,7 +33,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/EventDispatcher/CHANGELOG.md b/src/Symfony/Component/EventDispatcher/CHANGELOG.md
index 54fd04227b36c..f4bddd3b54160 100644
--- a/src/Symfony/Component/EventDispatcher/CHANGELOG.md
+++ b/src/Symfony/Component/EventDispatcher/CHANGELOG.md
@@ -1,6 +1,19 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * The `LegacyEventDispatcherProxy` class has been deprecated.
+
+5.0.0
+-----
+
+ * The signature of the `EventDispatcherInterface::dispatch()` method has been changed to `dispatch($event, string $eventName = null): object`.
+ * The `Event` class has been removed in favor of `Symfony\Contracts\EventDispatcher\Event`.
+ * The `TraceableEventDispatcherInterface` has been removed.
+ * The `WrappedListener` class is now final.
+
4.4.0
-----
diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php
index fdc65fd31280b..11dce4897bc65 100644
--- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php
+++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php
@@ -13,15 +13,12 @@
use Psr\EventDispatcher\StoppableEventInterface;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
-use Symfony\Component\EventDispatcher\LegacyEventProxy;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Stopwatch\Stopwatch;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+use Symfony\Contracts\Service\ResetInterface;
/**
* Collects some data about event listeners.
@@ -30,7 +27,7 @@
*
* @author Fabien Potencier
*/
-class TraceableEventDispatcher implements TraceableEventDispatcherInterface
+class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterface
{
protected $logger;
protected $stopwatch;
@@ -44,7 +41,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null)
{
- $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
+ $this->dispatcher = $dispatcher;
$this->stopwatch = $stopwatch;
$this->logger = $logger;
$this->wrappedListeners = [];
@@ -55,7 +52,7 @@ public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $sto
/**
* {@inheritdoc}
*/
- public function addListener($eventName, $listener, $priority = 0)
+ public function addListener(string $eventName, $listener, int $priority = 0)
{
$this->dispatcher->addListener($eventName, $listener, $priority);
}
@@ -71,7 +68,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber)
/**
* {@inheritdoc}
*/
- public function removeListener($eventName, $listener)
+ public function removeListener(string $eventName, $listener)
{
if (isset($this->wrappedListeners[$eventName])) {
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
@@ -97,7 +94,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber)
/**
* {@inheritdoc}
*/
- public function getListeners($eventName = null)
+ public function getListeners(string $eventName = null)
{
return $this->dispatcher->getListeners($eventName);
}
@@ -105,7 +102,7 @@ public function getListeners($eventName = null)
/**
* {@inheritdoc}
*/
- public function getListenerPriority($eventName, $listener)
+ public function getListenerPriority(string $eventName, $listener)
{
// we might have wrapped listeners for the event (if called while dispatching)
// in that case get the priority by wrapper
@@ -123,39 +120,25 @@ public function getListenerPriority($eventName, $listener)
/**
* {@inheritdoc}
*/
- public function hasListeners($eventName = null)
+ public function hasListeners(string $eventName = null)
{
return $this->dispatcher->hasListeners($eventName);
}
/**
* {@inheritdoc}
- *
- * @param string|null $eventName
*/
- public function dispatch($event/*, string $eventName = null*/)
+ public function dispatch(object $event, string $eventName = null): object
{
+ $eventName = $eventName ?? \get_class($event);
+
if (null === $this->callStack) {
$this->callStack = new \SplObjectStorage();
}
$currentRequestHash = $this->currentRequestHash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
- $eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
-
- if (\is_object($event)) {
- $eventName = $eventName ?? \get_class($event);
- } else {
- @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as first argument is deprecated since Symfony 4.3, pass it second and provide the event object first instead.', EventDispatcherInterface::class), E_USER_DEPRECATED);
- $swap = $event;
- $event = $eventName ?? new Event();
- $eventName = $swap;
-
- if (!$event instanceof Event) {
- throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an instance of %s, %s given.', EventDispatcherInterface::class, Event::class, \is_object($event) ? \get_class($event) : \gettype($event)));
- }
- }
- if (null !== $this->logger && ($event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface) && $event->isPropagationStopped()) {
+ if (null !== $this->logger && $event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
$this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
}
@@ -183,17 +166,15 @@ public function dispatch($event/*, string $eventName = null*/)
}
/**
- * {@inheritdoc}
- *
- * @param Request|null $request The request to get listeners for
+ * @return array
*/
- public function getCalledListeners(/* Request $request = null */)
+ public function getCalledListeners(Request $request = null)
{
if (null === $this->callStack) {
return [];
}
- $hash = 1 <= \func_num_args() && null !== ($request = func_get_arg(0)) ? spl_object_hash($request) : null;
+ $hash = $request ? spl_object_hash($request) : null;
$called = [];
foreach ($this->callStack as $listener) {
list($eventName, $requestHash) = $this->callStack->getInfo();
@@ -206,11 +187,9 @@ public function getCalledListeners(/* Request $request = null */)
}
/**
- * {@inheritdoc}
- *
- * @param Request|null $request The request to get listeners for
+ * @return array
*/
- public function getNotCalledListeners(/* Request $request = null */)
+ public function getNotCalledListeners(Request $request = null)
{
try {
$allListeners = $this->getListeners();
@@ -223,7 +202,7 @@ public function getNotCalledListeners(/* Request $request = null */)
return [];
}
- $hash = 1 <= \func_num_args() && null !== ($request = func_get_arg(0)) ? spl_object_hash($request) : null;
+ $hash = $request ? spl_object_hash($request) : null;
$calledListeners = [];
if (null !== $this->callStack) {
@@ -253,12 +232,9 @@ public function getNotCalledListeners(/* Request $request = null */)
return $notCalled;
}
- /**
- * @param Request|null $request The request to get orphaned events for
- */
- public function getOrphanedEvents(/* Request $request = null */): array
+ public function getOrphanedEvents(Request $request = null): array
{
- if (1 <= \func_num_args() && null !== $request = func_get_arg(0)) {
+ if ($request) {
return $this->orphanedEvents[spl_object_hash($request)] ?? [];
}
@@ -284,46 +260,26 @@ public function reset()
*
* @return mixed
*/
- public function __call($method, $arguments)
+ public function __call(string $method, array $arguments)
{
return $this->dispatcher->{$method}(...$arguments);
}
/**
* Called before dispatching the event.
- *
- * @param object $event
*/
- protected function beforeDispatch(string $eventName, $event)
+ protected function beforeDispatch(string $eventName, object $event)
{
- $this->preDispatch($eventName, $event instanceof Event ? $event : new LegacyEventProxy($event));
}
/**
* Called after dispatching the event.
- *
- * @param object $event
- */
- protected function afterDispatch(string $eventName, $event)
- {
- $this->postDispatch($eventName, $event instanceof Event ? $event : new LegacyEventProxy($event));
- }
-
- /**
- * @deprecated since Symfony 4.3, will be removed in 5.0, use beforeDispatch instead
- */
- protected function preDispatch($eventName, Event $event)
- {
- }
-
- /**
- * @deprecated since Symfony 4.3, will be removed in 5.0, use afterDispatch instead
*/
- protected function postDispatch($eventName, Event $event)
+ protected function afterDispatch(string $eventName, object $event)
{
}
- private function preProcess(string $eventName)
+ private function preProcess(string $eventName): void
{
if (!$this->dispatcher->hasListeners($eventName)) {
$this->orphanedEvents[$this->currentRequestHash][] = $eventName;
@@ -341,7 +297,7 @@ private function preProcess(string $eventName)
}
}
- private function postProcess(string $eventName)
+ private function postProcess(string $eventName): void
{
unset($this->wrappedListeners[$eventName]);
$skipped = false;
diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php
deleted file mode 100644
index 4fedb9a413a3b..0000000000000
--- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php
+++ /dev/null
@@ -1,42 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\EventDispatcher\Debug;
-
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Contracts\Service\ResetInterface;
-
-/**
- * @deprecated since Symfony 4.1
- *
- * @author Fabien Potencier
- */
-interface TraceableEventDispatcherInterface extends EventDispatcherInterface, ResetInterface
-{
- /**
- * Gets the called listeners.
- *
- * @param Request|null $request The request to get listeners for
- *
- * @return array An array of called listeners
- */
- public function getCalledListeners(/* Request $request = null */);
-
- /**
- * Gets the not called listeners.
- *
- * @param Request|null $request The request to get listeners for
- *
- * @return array An array of not called listeners
- */
- public function getNotCalledListeners(/* Request $request = null */);
-}
diff --git a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
index e047639081c78..295bcae88a734 100644
--- a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
+++ b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
@@ -12,19 +12,14 @@
namespace Symfony\Component\EventDispatcher\Debug;
use Psr\EventDispatcher\StoppableEventInterface;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\LegacyEventProxy;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Caster\ClassStub;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
/**
* @author Fabien Potencier
- *
- * @final since Symfony 4.3: the "Event" type-hint on __invoke() will be replaced by "object" in 5.0
*/
-class WrappedListener
+final class WrappedListener
{
private $listener;
private $optimizedListener;
@@ -81,22 +76,22 @@ public function getWrappedListener()
return $this->listener;
}
- public function wasCalled()
+ public function wasCalled(): bool
{
return $this->called;
}
- public function stoppedPropagation()
+ public function stoppedPropagation(): bool
{
return $this->stoppedPropagation;
}
- public function getPretty()
+ public function getPretty(): string
{
return $this->pretty;
}
- public function getInfo($eventName)
+ public function getInfo(string $eventName): array
{
if (null === $this->stub) {
$this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()';
@@ -110,12 +105,8 @@ public function getInfo($eventName)
];
}
- public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
+ public function __invoke(object $event, string $eventName, EventDispatcherInterface $dispatcher): void
{
- if ($event instanceof LegacyEventProxy) {
- $event = $event->getEvent();
- }
-
$dispatcher = $this->dispatcher ?: $dispatcher;
$this->called = true;
@@ -129,7 +120,7 @@ public function __invoke(Event $event, $eventName, EventDispatcherInterface $dis
$e->stop();
}
- if (($event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface) && $event->isPropagationStopped()) {
+ if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
$this->stoppedPropagation = true;
}
}
diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
index 9c88809de059b..2bbebb7d6f794 100644
--- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
+++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
@@ -16,7 +16,6 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\EventDispatcher\Event as LegacyEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event;
@@ -141,7 +140,6 @@ private function getEventFromTypeDeclaration(ContainerBuilder $container, string
|| !($type = $m->getParameters()[0]->getType())
|| $type->isBuiltin()
|| Event::class === ($name = $type->getName())
- || LegacyEvent::class === $name
) {
throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
}
@@ -160,7 +158,7 @@ class ExtractingEventDispatcher extends EventDispatcher implements EventSubscrib
public static $aliases = [];
public static $subscriber;
- public function addListener($eventName, $listener, $priority = 0)
+ public function addListener(string $eventName, $listener, int $priority = 0)
{
$this->listeners[] = [$eventName, $listener[1], $priority];
}
diff --git a/src/Symfony/Component/EventDispatcher/Event.php b/src/Symfony/Component/EventDispatcher/Event.php
deleted file mode 100644
index 307c4be5de0c2..0000000000000
--- a/src/Symfony/Component/EventDispatcher/Event.php
+++ /dev/null
@@ -1,38 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\EventDispatcher;
-
-/**
- * @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
- */
-class Event
-{
- private $propagationStopped = false;
-
- /**
- * @return bool Whether propagation was already stopped for this event
- *
- * @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
- */
- public function isPropagationStopped()
- {
- return $this->propagationStopped;
- }
-
- /**
- * @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
- */
- public function stopPropagation()
- {
- $this->propagationStopped = true;
- }
-}
diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php
index bd2874ba6c484..8dba33d0d5278 100644
--- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php
+++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php
@@ -13,7 +13,6 @@
use Psr\EventDispatcher\StoppableEventInterface;
use Symfony\Component\EventDispatcher\Debug\WrappedListener;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
/**
* The EventDispatcherInterface is the central point of Symfony's event listener system.
@@ -45,23 +44,10 @@ public function __construct()
/**
* {@inheritdoc}
- *
- * @param string|null $eventName
*/
- public function dispatch($event/*, string $eventName = null*/)
+ public function dispatch(object $event, string $eventName = null): object
{
- $eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
-
- if (\is_object($event)) {
- $eventName = $eventName ?? \get_class($event);
- } elseif (\is_string($event) && (null === $eventName || $eventName instanceof ContractsEvent || $eventName instanceof Event)) {
- @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.', EventDispatcherInterface::class), E_USER_DEPRECATED);
- $swap = $event;
- $event = $eventName ?? new Event();
- $eventName = $swap;
- } else {
- throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an object, %s given.', EventDispatcherInterface::class, \is_object($event) ? \get_class($event) : \gettype($event)));
- }
+ $eventName = $eventName ?? \get_class($event);
if (null !== $this->optimized && null !== $eventName) {
$listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
@@ -79,7 +65,7 @@ public function dispatch($event/*, string $eventName = null*/)
/**
* {@inheritdoc}
*/
- public function getListeners($eventName = null)
+ public function getListeners(string $eventName = null)
{
if (null !== $eventName) {
if (empty($this->listeners[$eventName])) {
@@ -105,7 +91,7 @@ public function getListeners($eventName = null)
/**
* {@inheritdoc}
*/
- public function getListenerPriority($eventName, $listener)
+ public function getListenerPriority(string $eventName, $listener)
{
if (empty($this->listeners[$eventName])) {
return null;
@@ -134,7 +120,7 @@ public function getListenerPriority($eventName, $listener)
/**
* {@inheritdoc}
*/
- public function hasListeners($eventName = null)
+ public function hasListeners(string $eventName = null)
{
if (null !== $eventName) {
return !empty($this->listeners[$eventName]);
@@ -152,7 +138,7 @@ public function hasListeners($eventName = null)
/**
* {@inheritdoc}
*/
- public function addListener($eventName, $listener, $priority = 0)
+ public function addListener(string $eventName, $listener, int $priority = 0)
{
$this->listeners[$eventName][$priority][] = $listener;
unset($this->sorted[$eventName], $this->optimized[$eventName]);
@@ -161,7 +147,7 @@ public function addListener($eventName, $listener, $priority = 0)
/**
* {@inheritdoc}
*/
- public function removeListener($eventName, $listener)
+ public function removeListener(string $eventName, $listener)
{
if (empty($this->listeners[$eventName])) {
return;
@@ -233,34 +219,14 @@ public function removeSubscriber(EventSubscriberInterface $subscriber)
* @param string $eventName The name of the event to dispatch
* @param object $event The event object to pass to the event handlers/listeners
*/
- protected function callListeners(iterable $listeners, string $eventName, $event)
+ protected function callListeners(iterable $listeners, string $eventName, object $event)
{
- if ($event instanceof Event) {
- $this->doDispatch($listeners, $eventName, $event);
-
- return;
- }
-
- $stoppable = $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
+ $stoppable = $event instanceof StoppableEventInterface;
foreach ($listeners as $listener) {
if ($stoppable && $event->isPropagationStopped()) {
break;
}
- // @deprecated: the ternary operator is part of a BC layer and should be removed in 5.0
- $listener($listener instanceof WrappedListener ? new LegacyEventProxy($event) : $event, $eventName, $this);
- }
- }
-
- /**
- * @deprecated since Symfony 4.3, use callListeners() instead
- */
- protected function doDispatch($listeners, $eventName, Event $event)
- {
- foreach ($listeners as $listener) {
- if ($event->isPropagationStopped()) {
- break;
- }
$listener($event, $eventName, $this);
}
}
diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php
index ceaa62aeb0472..88c707c9a360b 100644
--- a/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php
+++ b/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php
@@ -25,12 +25,11 @@ interface EventDispatcherInterface extends ContractsEventDispatcherInterface
/**
* Adds an event listener that listens on the specified events.
*
- * @param string $eventName The event to listen on
- * @param callable $listener The listener
- * @param int $priority The higher this value, the earlier an event
- * listener will be triggered in the chain (defaults to 0)
+ * @param callable $listener The listener
+ * @param int $priority The higher this value, the earlier an event
+ * listener will be triggered in the chain (defaults to 0)
*/
- public function addListener($eventName, $listener, $priority = 0);
+ public function addListener(string $eventName, $listener, int $priority = 0);
/**
* Adds an event subscriber.
@@ -43,40 +42,34 @@ public function addSubscriber(EventSubscriberInterface $subscriber);
/**
* Removes an event listener from the specified events.
*
- * @param string $eventName The event to remove a listener from
- * @param callable $listener The listener to remove
+ * @param callable $listener The listener to remove
*/
- public function removeListener($eventName, $listener);
+ public function removeListener(string $eventName, $listener);
public function removeSubscriber(EventSubscriberInterface $subscriber);
/**
* Gets the listeners of a specific event or all listeners sorted by descending priority.
*
- * @param string|null $eventName The name of the event
- *
* @return array The event listeners for the specified event, or all event listeners by event name
*/
- public function getListeners($eventName = null);
+ public function getListeners(string $eventName = null);
/**
* Gets the listener priority for a specific event.
*
* Returns null if the event or the listener does not exist.
*
- * @param string $eventName The name of the event
- * @param callable $listener The listener
+ * @param callable $listener The listener
*
* @return int|null The event listener priority
*/
- public function getListenerPriority($eventName, $listener);
+ public function getListenerPriority(string $eventName, $listener);
/**
* Checks whether an event has any registered listeners.
*
- * @param string|null $eventName The name of the event
- *
* @return bool true if the specified event has any listeners, false otherwise
*/
- public function hasListeners($eventName = null);
+ public function hasListeners(string $eventName = null);
}
diff --git a/src/Symfony/Component/EventDispatcher/GenericEvent.php b/src/Symfony/Component/EventDispatcher/GenericEvent.php
index f005e3a3db076..34b95cedee817 100644
--- a/src/Symfony/Component/EventDispatcher/GenericEvent.php
+++ b/src/Symfony/Component/EventDispatcher/GenericEvent.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\EventDispatcher;
+use Symfony\Contracts\EventDispatcher\Event;
+
/**
* Event encapsulation class.
*
@@ -48,13 +50,11 @@ public function getSubject()
/**
* Get argument by key.
*
- * @param string $key Key
- *
* @return mixed Contents of array key
*
* @throws \InvalidArgumentException if key is not found
*/
- public function getArgument($key)
+ public function getArgument(string $key)
{
if ($this->hasArgument($key)) {
return $this->arguments[$key];
@@ -66,12 +66,11 @@ public function getArgument($key)
/**
* Add argument to event.
*
- * @param string $key Argument name
- * @param mixed $value Value
+ * @param mixed $value Value
*
* @return $this
*/
- public function setArgument($key, $value)
+ public function setArgument(string $key, $value)
{
$this->arguments[$key] = $value;
@@ -91,8 +90,6 @@ public function getArguments()
/**
* Set args property.
*
- * @param array $args Arguments
- *
* @return $this
*/
public function setArguments(array $args = [])
@@ -105,11 +102,9 @@ public function setArguments(array $args = [])
/**
* Has argument.
*
- * @param string $key Key of arguments array
- *
* @return bool
*/
- public function hasArgument($key)
+ public function hasArgument(string $key)
{
return \array_key_exists($key, $this->arguments);
}
diff --git a/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php
index 75a7d7318187b..568d79c3a2916 100644
--- a/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php
+++ b/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php
@@ -22,32 +22,21 @@ class ImmutableEventDispatcher implements EventDispatcherInterface
public function __construct(EventDispatcherInterface $dispatcher)
{
- $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
+ $this->dispatcher = $dispatcher;
}
/**
* {@inheritdoc}
- *
- * @param string|null $eventName
*/
- public function dispatch($event/*, string $eventName = null*/)
+ public function dispatch(object $event, string $eventName = null): object
{
- $eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
-
- if (is_scalar($event)) {
- // deprecated
- $swap = $event;
- $event = $eventName ?? new Event();
- $eventName = $swap;
- }
-
return $this->dispatcher->dispatch($event, $eventName);
}
/**
* {@inheritdoc}
*/
- public function addListener($eventName, $listener, $priority = 0)
+ public function addListener(string $eventName, $listener, int $priority = 0)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
@@ -63,7 +52,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber)
/**
* {@inheritdoc}
*/
- public function removeListener($eventName, $listener)
+ public function removeListener(string $eventName, $listener)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
@@ -79,7 +68,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber)
/**
* {@inheritdoc}
*/
- public function getListeners($eventName = null)
+ public function getListeners(string $eventName = null)
{
return $this->dispatcher->getListeners($eventName);
}
@@ -87,7 +76,7 @@ public function getListeners($eventName = null)
/**
* {@inheritdoc}
*/
- public function getListenerPriority($eventName, $listener)
+ public function getListenerPriority(string $eventName, $listener)
{
return $this->dispatcher->getListenerPriority($eventName, $listener);
}
@@ -95,7 +84,7 @@ public function getListenerPriority($eventName, $listener)
/**
* {@inheritdoc}
*/
- public function hasListeners($eventName = null)
+ public function hasListeners(string $eventName = null)
{
return $this->dispatcher->hasListeners($eventName);
}
diff --git a/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php b/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php
index 513e260e98106..ae681fe1e7dc5 100644
--- a/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php
+++ b/src/Symfony/Component/EventDispatcher/LegacyEventDispatcherProxy.php
@@ -11,137 +11,21 @@
namespace Symfony\Component\EventDispatcher;
-use Psr\EventDispatcher\StoppableEventInterface;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
-use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+
+trigger_deprecation('symfony/event-dispatcher', '5.1', '%s is deprecated, use the event dispatcher without the proxy.', LegacyEventDispatcherProxy::class);
/**
* A helper class to provide BC/FC with the legacy signature of EventDispatcherInterface::dispatch().
*
- * This class should be deprecated in Symfony 5.1
- *
* @author Nicolas Grekas
+ *
+ * @deprecated since Symfony 5.1.
*/
-final class LegacyEventDispatcherProxy implements EventDispatcherInterface
+final class LegacyEventDispatcherProxy
{
- private $dispatcher;
-
- public static function decorate(?ContractsEventDispatcherInterface $dispatcher): ?ContractsEventDispatcherInterface
- {
- if (null === $dispatcher) {
- return null;
- }
- $r = new \ReflectionMethod($dispatcher, 'dispatch');
- $param2 = $r->getParameters()[1] ?? null;
-
- if (!$param2 || !$param2->hasType() || $param2->getType()->isBuiltin()) {
- return $dispatcher;
- }
-
- @trigger_error(sprintf('The signature of the "%s::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.', $r->class), E_USER_DEPRECATED);
-
- $self = new self();
- $self->dispatcher = $dispatcher;
-
- return $self;
- }
-
- /**
- * {@inheritdoc}
- *
- * @param string|null $eventName
- *
- * @return object
- */
- public function dispatch($event/*, string $eventName = null*/)
- {
- $eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
-
- if (\is_object($event)) {
- $eventName = $eventName ?? \get_class($event);
- } elseif (\is_string($event) && (null === $eventName || $eventName instanceof ContractsEvent || $eventName instanceof Event)) {
- @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.', ContractsEventDispatcherInterface::class), E_USER_DEPRECATED);
- $swap = $event;
- $event = $eventName ?? new Event();
- $eventName = $swap;
- } else {
- throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an object, %s given.', ContractsEventDispatcherInterface::class, \is_object($event) ? \get_class($event) : \gettype($event)));
- }
-
- $listeners = $this->getListeners($eventName);
- $stoppable = $event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
-
- foreach ($listeners as $listener) {
- if ($stoppable && $event->isPropagationStopped()) {
- break;
- }
- $listener($event, $eventName, $this);
- }
-
- return $event;
- }
-
- /**
- * {@inheritdoc}
- */
- public function addListener($eventName, $listener, $priority = 0)
- {
- return $this->dispatcher->addListener($eventName, $listener, $priority);
- }
-
- /**
- * {@inheritdoc}
- */
- public function addSubscriber(EventSubscriberInterface $subscriber)
- {
- return $this->dispatcher->addSubscriber($subscriber);
- }
-
- /**
- * {@inheritdoc}
- */
- public function removeListener($eventName, $listener)
- {
- return $this->dispatcher->removeListener($eventName, $listener);
- }
-
- /**
- * {@inheritdoc}
- */
- public function removeSubscriber(EventSubscriberInterface $subscriber)
- {
- return $this->dispatcher->removeSubscriber($subscriber);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getListeners($eventName = null): array
- {
- return $this->dispatcher->getListeners($eventName);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getListenerPriority($eventName, $listener): ?int
- {
- return $this->dispatcher->getListenerPriority($eventName, $listener);
- }
-
- /**
- * {@inheritdoc}
- */
- public function hasListeners($eventName = null): bool
- {
- return $this->dispatcher->hasListeners($eventName);
- }
-
- /**
- * Proxies all method calls to the original event dispatcher.
- */
- public function __call($method, $arguments)
+ public static function decorate(?EventDispatcherInterface $dispatcher): ?EventDispatcherInterface
{
- return $this->dispatcher->{$method}(...$arguments);
+ return $dispatcher;
}
}
diff --git a/src/Symfony/Component/EventDispatcher/LegacyEventProxy.php b/src/Symfony/Component/EventDispatcher/LegacyEventProxy.php
deleted file mode 100644
index 45ee251d6a98d..0000000000000
--- a/src/Symfony/Component/EventDispatcher/LegacyEventProxy.php
+++ /dev/null
@@ -1,62 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\EventDispatcher;
-
-use Psr\EventDispatcher\StoppableEventInterface;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
-
-/**
- * @internal to be removed in 5.0.
- */
-final class LegacyEventProxy extends Event
-{
- private $event;
-
- /**
- * @param object $event
- */
- public function __construct($event)
- {
- $this->event = $event;
- }
-
- /**
- * @return object $event
- */
- public function getEvent()
- {
- return $this->event;
- }
-
- public function isPropagationStopped(): bool
- {
- if (!$this->event instanceof ContractsEvent && !$this->event instanceof StoppableEventInterface) {
- return false;
- }
-
- return $this->event->isPropagationStopped();
- }
-
- public function stopPropagation()
- {
- if (!$this->event instanceof ContractsEvent) {
- return;
- }
-
- $this->event->stopPropagation();
- }
-
- public function __call($name, $args)
- {
- return $this->event->{$name}(...$args);
- }
-}
diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
index 6a208a6662512..288168e410257 100644
--- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
+++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
@@ -13,12 +13,11 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Stopwatch\Stopwatch;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+use Symfony\Contracts\EventDispatcher\Event;
class TraceableEventDispatcherTest extends TestCase
{
@@ -140,19 +139,6 @@ public function testClearCalledListeners()
$this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
}
- public function testDispatchContractsEvent()
- {
- $expectedEvent = new ContractsEvent();
- $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
- $tdispatcher->addListener('foo', function ($event) use ($expectedEvent) {
- $this->assertSame($event, $expectedEvent);
- }, 5);
- $tdispatcher->dispatch($expectedEvent, 'foo');
-
- $listeners = $tdispatcher->getCalledListeners();
- $this->assertArrayHasKey('stub', $listeners[0]);
- }
-
public function testDispatchAfterReset()
{
$tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
diff --git a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php
index 7c8b5dc9b20fe..ea9fe8c1b6cdc 100644
--- a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php
+++ b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php
@@ -12,10 +12,9 @@
namespace Symfony\Component\EventDispatcher\Tests;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+use Symfony\Contracts\EventDispatcher\Event;
class EventDispatcherTest extends TestCase
{
@@ -135,22 +134,8 @@ public function testDispatch()
$this->dispatcher->dispatch(new Event(), self::preFoo);
$this->assertTrue($this->listener->preFooInvoked);
$this->assertFalse($this->listener->postFooInvoked);
- $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), 'noevent'));
- $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), self::preFoo));
- $event = new Event();
- $return = $this->dispatcher->dispatch($event, self::preFoo);
- $this->assertSame($event, $return);
- }
-
- public function testDispatchContractsEvent()
- {
- $this->dispatcher->addListener('pre.foo', [$this->listener, 'preFoo']);
- $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo']);
- $this->dispatcher->dispatch(new ContractsEvent(), self::preFoo);
- $this->assertTrue($this->listener->preFooInvoked);
- $this->assertFalse($this->listener->postFooInvoked);
- $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), 'noevent'));
- $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), self::preFoo));
+ $this->assertInstanceOf('Symfony\Contracts\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), 'noevent'));
+ $this->assertInstanceOf('Symfony\Contracts\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), self::preFoo));
$event = new Event();
$return = $this->dispatcher->dispatch($event, self::preFoo);
$this->assertSame($event, $return);
@@ -421,33 +406,6 @@ public function testMutatingWhilePropagationIsStopped()
$this->assertTrue($testLoaded);
}
-
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.
- */
- public function testLegacySignatureWithoutEvent()
- {
- $this->dispatcher->dispatch('foo');
- }
-
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.
- */
- public function testLegacySignatureWithEvent()
- {
- $this->dispatcher->dispatch('foo', new Event());
- }
-
- /**
- * @group legacy
- * @expectedDeprecation Calling the "Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.
- */
- public function testLegacySignatureWithNewEventObject()
- {
- $this->dispatcher->dispatch('foo', new ContractsEvent());
- }
}
class CallableClass
diff --git a/src/Symfony/Component/EventDispatcher/Tests/EventTest.php b/src/Symfony/Component/EventDispatcher/Tests/EventTest.php
deleted file mode 100644
index 34ed3ba70ec4a..0000000000000
--- a/src/Symfony/Component/EventDispatcher/Tests/EventTest.php
+++ /dev/null
@@ -1,55 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\EventDispatcher\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\EventDispatcher\Event;
-
-/**
- * @group legacy
- */
-class EventTest extends TestCase
-{
- /**
- * @var \Symfony\Component\EventDispatcher\Event
- */
- protected $event;
-
- /**
- * Sets up the fixture, for example, opens a network connection.
- * This method is called before a test is executed.
- */
- protected function setUp(): void
- {
- $this->event = new Event();
- }
-
- /**
- * Tears down the fixture, for example, closes a network connection.
- * This method is called after a test is executed.
- */
- protected function tearDown(): void
- {
- $this->event = null;
- }
-
- public function testIsPropagationStopped()
- {
- $this->assertFalse($this->event->isPropagationStopped());
- }
-
- public function testStopPropagationAndIsPropagationStopped()
- {
- $this->event->stopPropagation();
- $this->assertTrue($this->event->isPropagationStopped());
- }
-}
diff --git a/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php
index 1a33c19b15f6f..7cef3e6558208 100644
--- a/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php
+++ b/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php
@@ -13,8 +13,8 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
+use Symfony\Contracts\EventDispatcher\Event;
/**
* @author Bernhard Schussek
diff --git a/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php b/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php
deleted file mode 100644
index f0469f9538471..0000000000000
--- a/src/Symfony/Component/EventDispatcher/Tests/LegacyEventDispatcherProxyTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\EventDispatcher\Tests;
-
-use Symfony\Component\EventDispatcher\Event;
-use Symfony\Component\EventDispatcher\EventDispatcher;
-use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
-use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
-
-/**
- * @group legacy
- */
-class LegacyEventDispatcherProxyTest extends EventDispatcherTest
-{
- /**
- * @group legacy
- * @expectedDeprecation The signature of the "Symfony\Component\EventDispatcher\Tests\TestLegacyEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.
- * @expectedDeprecation Calling the "Symfony\Contracts\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.
- */
- public function testLegacySignatureWithoutEvent()
- {
- $this->createEventDispatcher()->dispatch('foo');
- }
-
- /**
- * @group legacy
- * @expectedDeprecation The signature of the "Symfony\Component\EventDispatcher\Tests\TestLegacyEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.
- * @expectedDeprecation Calling the "Symfony\Contracts\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.
- */
- public function testLegacySignatureWithEvent()
- {
- $this->createEventDispatcher()->dispatch('foo', new Event());
- }
-
- /**
- * @group legacy
- * @expectedDeprecation The signature of the "Symfony\Component\EventDispatcher\Tests\TestLegacyEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.
- * @expectedDeprecation The signature of the "Symfony\Component\EventDispatcher\Tests\TestLegacyEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.
- * @expectedDeprecation Calling the "Symfony\Contracts\EventDispatcher\EventDispatcherInterface::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.
- */
- public function testLegacySignatureWithNewEventObject()
- {
- $this->createEventDispatcher()->dispatch('foo', new ContractsEvent());
- }
-
- protected function createEventDispatcher()
- {
- return LegacyEventDispatcherProxy::decorate(new TestLegacyEventDispatcher());
- }
-}
-
-class TestLegacyEventDispatcher extends EventDispatcher
-{
- public function dispatch($eventName, Event $event = null): Event
- {
- return parent::dispatch($event, $eventName);
- }
-}
diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json
index ef36e455326de..5e4a83363ad56 100644
--- a/src/Symfony/Component/EventDispatcher/composer.json
+++ b/src/Symfony/Component/EventDispatcher/composer.json
@@ -16,24 +16,25 @@
}
],
"require": {
- "php": "^7.1.3",
- "symfony/event-dispatcher-contracts": "^1.1"
+ "php": "^7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
+ "symfony/event-dispatcher-contracts": "^2"
},
"require-dev": {
- "symfony/dependency-injection": "^3.4|^4.0|^5.0",
- "symfony/expression-language": "^3.4|^4.0|^5.0",
- "symfony/config": "^3.4|^4.0|^5.0",
- "symfony/http-foundation": "^3.4|^4.0|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/expression-language": "^4.4|^5.0",
+ "symfony/config": "^4.4|^5.0",
+ "symfony/http-foundation": "^4.4|^5.0",
"symfony/service-contracts": "^1.1|^2",
- "symfony/stopwatch": "^3.4|^4.0|^5.0",
+ "symfony/stopwatch": "^4.4|^5.0",
"psr/log": "~1.0"
},
"conflict": {
- "symfony/dependency-injection": "<3.4"
+ "symfony/dependency-injection": "<4.4"
},
"provide": {
"psr/event-dispatcher-implementation": "1.0",
- "symfony/event-dispatcher-implementation": "1.1"
+ "symfony/event-dispatcher-implementation": "2.0"
},
"suggest": {
"symfony/dependency-injection": "",
@@ -48,7 +49,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/ExpressionLanguage/Compiler.php b/src/Symfony/Component/ExpressionLanguage/Compiler.php
index 604f04c6f0be1..e040e8c4bf264 100644
--- a/src/Symfony/Component/ExpressionLanguage/Compiler.php
+++ b/src/Symfony/Component/ExpressionLanguage/Compiler.php
@@ -28,7 +28,7 @@ public function __construct(array $functions)
$this->functions = $functions;
}
- public function getFunction($name)
+ public function getFunction(string $name)
{
return $this->functions[$name];
}
@@ -78,11 +78,9 @@ public function subcompile(Node\Node $node)
/**
* Adds a raw string to the compiled code.
*
- * @param string $string The string
- *
* @return $this
*/
- public function raw($string)
+ public function raw(string $string)
{
$this->source .= $string;
@@ -92,11 +90,9 @@ public function raw($string)
/**
* Adds a quoted string to the compiled code.
*
- * @param string $value The string
- *
* @return $this
*/
- public function string($value)
+ public function string(string $value)
{
$this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php
index 2bc91e8d038e0..7408ff2fcf3bf 100644
--- a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php
+++ b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php
@@ -64,7 +64,6 @@ public function getEvaluator()
/**
* Creates an ExpressionFunction from a PHP function name.
*
- * @param string $phpFunctionName The PHP function name
* @param string|null $expressionFunctionName The expression function name (default: same than the PHP function name)
*
* @return self
@@ -73,7 +72,7 @@ public function getEvaluator()
* @throws \InvalidArgumentException if given PHP function name is in namespace
* and expression function name is not defined
*/
- public static function fromPhp($phpFunctionName, $expressionFunctionName = null)
+ public static function fromPhp(string $phpFunctionName, string $expressionFunctionName = null)
{
$phpFunctionName = ltrim($phpFunctionName, '\\');
if (!\function_exists($phpFunctionName)) {
diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php
index a257ec7969692..e9e36e9f6452b 100644
--- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php
+++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php
@@ -44,11 +44,10 @@ public function __construct(CacheItemPoolInterface $cache = null, array $provide
* Compiles an expression source code.
*
* @param Expression|string $expression The expression to compile
- * @param array $names An array of valid names
*
* @return string The compiled PHP source code
*/
- public function compile($expression, $names = [])
+ public function compile($expression, array $names = [])
{
return $this->getCompiler()->compile($this->parse($expression, $names)->getNodes())->getSource();
}
@@ -57,11 +56,10 @@ public function compile($expression, $names = [])
* Evaluate an expression.
*
* @param Expression|string $expression The expression to compile
- * @param array $values An array of values
*
* @return mixed The result of the evaluation of the expression
*/
- public function evaluate($expression, $values = [])
+ public function evaluate($expression, array $values = [])
{
return $this->parse($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values);
}
@@ -70,11 +68,10 @@ public function evaluate($expression, $values = [])
* Parses an expression.
*
* @param Expression|string $expression The expression to parse
- * @param array $names An array of valid names
*
* @return ParsedExpression A ParsedExpression instance
*/
- public function parse($expression, $names)
+ public function parse($expression, array $names)
{
if ($expression instanceof ParsedExpression) {
return $expression;
@@ -103,7 +100,6 @@ public function parse($expression, $names)
/**
* Registers a function.
*
- * @param string $name The function name
* @param callable $compiler A callable able to compile the function
* @param callable $evaluator A callable able to evaluate the function
*
@@ -111,7 +107,7 @@ public function parse($expression, $names)
*
* @see ExpressionFunction
*/
- public function register($name, callable $compiler, callable $evaluator)
+ public function register(string $name, callable $compiler, callable $evaluator)
{
if (null !== $this->parser) {
throw new \LogicException('Registering functions after calling evaluate(), compile() or parse() is not supported.');
diff --git a/src/Symfony/Component/ExpressionLanguage/Lexer.php b/src/Symfony/Component/ExpressionLanguage/Lexer.php
index 011057eef4e18..9dd695bf170d9 100644
--- a/src/Symfony/Component/ExpressionLanguage/Lexer.php
+++ b/src/Symfony/Component/ExpressionLanguage/Lexer.php
@@ -21,13 +21,11 @@ class Lexer
/**
* Tokenizes an expression.
*
- * @param string $expression The expression to tokenize
- *
* @return TokenStream A token stream instance
*
* @throws SyntaxError
*/
- public function tokenize($expression)
+ public function tokenize(string $expression)
{
$expression = str_replace(["\r", "\n", "\t", "\v", "\f"], ' ', $expression);
$cursor = 0;
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/ArrayNode.php b/src/Symfony/Component/ExpressionLanguage/Node/ArrayNode.php
index 921319a74474d..1644b2e2a8339 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/ArrayNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/ArrayNode.php
@@ -46,7 +46,7 @@ public function compile(Compiler $compiler)
$compiler->raw(']');
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
$result = [];
foreach ($this->getKeyValuePairs() as $pair) {
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php
index 11245f9a86e2a..c9dcdf34e4f46 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php
@@ -84,7 +84,7 @@ public function compile(Compiler $compiler)
;
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
$operator = $this->attributes['operator'];
$left = $this->nodes['left']->evaluate($functions, $values);
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/ConditionalNode.php b/src/Symfony/Component/ExpressionLanguage/Node/ConditionalNode.php
index ca1b484bc08c2..ba78a2848eeab 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/ConditionalNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/ConditionalNode.php
@@ -40,7 +40,7 @@ public function compile(Compiler $compiler)
;
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
if ($this->nodes['expr1']->evaluate($functions, $values)) {
return $this->nodes['expr2']->evaluate($functions, $values);
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php b/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php
index 0353f78510b70..b86abd43a5ebc 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php
@@ -36,7 +36,7 @@ public function compile(Compiler $compiler)
$compiler->repr($this->attributes['value']);
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
return $this->attributes['value'];
}
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php b/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php
index 2a46191061d19..37b5982091ad3 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php
@@ -40,7 +40,7 @@ public function compile(Compiler $compiler)
$compiler->raw($function['compiler'](...$arguments));
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
$arguments = [$values];
foreach ($this->nodes['arguments']->nodes as $node) {
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
index a28b59611363b..7d86b53bc2ece 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
@@ -64,7 +64,7 @@ public function compile(Compiler $compiler)
}
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
switch ($this->attributes['type']) {
case self::PROPERTY_CALL:
@@ -80,7 +80,7 @@ public function evaluate($functions, $values)
case self::METHOD_CALL:
$obj = $this->nodes['node']->evaluate($functions, $values);
if (!\is_object($obj)) {
- throw new \RuntimeException('Unable to get a property on a non-object.');
+ throw new \RuntimeException('Unable to call method of a non-object.');
}
if (!\is_callable($toCall = [$obj, $this->nodes['attribute']->attributes['value']])) {
throw new \RuntimeException(sprintf('Unable to call method "%s" of object "%s".', $this->nodes['attribute']->attributes['value'], \get_class($obj)));
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php b/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php
index 1a3d994148f8c..e017e967a1d08 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/NameNode.php
@@ -33,7 +33,7 @@ public function compile(Compiler $compiler)
$compiler->raw('$'.$this->attributes['name']);
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
return $values[$this->attributes['name']];
}
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/Node.php b/src/Symfony/Component/ExpressionLanguage/Node/Node.php
index decb06d1bb781..dc76de00bf0b5 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/Node.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/Node.php
@@ -67,7 +67,7 @@ public function compile(Compiler $compiler)
}
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
$results = [];
foreach ($this->nodes as $node) {
@@ -93,7 +93,7 @@ public function dump()
return $dump;
}
- protected function dumpString($value)
+ protected function dumpString(string $value)
{
return sprintf('"%s"', addcslashes($value, "\0\t\"\\"));
}
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php
index abf2cc6bac908..1cd21c579bdcd 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php
@@ -45,7 +45,7 @@ public function compile(Compiler $compiler)
;
}
- public function evaluate($functions, $values)
+ public function evaluate(array $functions, array $values)
{
$value = $this->nodes['node']->evaluate($functions, $values);
switch ($this->attributes['operator']) {
diff --git a/src/Symfony/Component/ExpressionLanguage/Parser.php b/src/Symfony/Component/ExpressionLanguage/Parser.php
index 59c4d67d781c6..521cd73ceac8a 100644
--- a/src/Symfony/Component/ExpressionLanguage/Parser.php
+++ b/src/Symfony/Component/ExpressionLanguage/Parser.php
@@ -85,13 +85,11 @@ public function __construct(array $functions)
* variable 'container' can be used in the expression
* but the compiled code will use 'this'.
*
- * @param array $names An array of valid names
- *
* @return Node\Node A node tree
*
* @throws SyntaxError
*/
- public function parse(TokenStream $stream, $names = [])
+ public function parse(TokenStream $stream, array $names = [])
{
$this->stream = $stream;
$this->names = $names;
@@ -104,7 +102,7 @@ public function parse(TokenStream $stream, $names = [])
return $node;
}
- public function parseExpression($precedence = 0)
+ public function parseExpression(int $precedence = 0)
{
$expr = $this->getPrimary();
$token = $this->stream->current;
@@ -148,7 +146,7 @@ protected function getPrimary()
return $this->parsePrimaryExpression();
}
- protected function parseConditionalExpression($expr)
+ protected function parseConditionalExpression(Node\Node $expr)
{
while ($this->stream->current->test(Token::PUNCTUATION_TYPE, '?')) {
$this->stream->next();
@@ -301,7 +299,7 @@ public function parseHashExpression()
return $node;
}
- public function parsePostfixExpression($node)
+ public function parsePostfixExpression(Node\Node $node)
{
$token = $this->stream->current;
while (Token::PUNCTUATION_TYPE == $token->type) {
diff --git a/src/Symfony/Component/ExpressionLanguage/Token.php b/src/Symfony/Component/ExpressionLanguage/Token.php
index 40bb75587ec96..38aac0a055e6b 100644
--- a/src/Symfony/Component/ExpressionLanguage/Token.php
+++ b/src/Symfony/Component/ExpressionLanguage/Token.php
@@ -54,12 +54,11 @@ public function __toString()
/**
* Tests the current token for a type and/or a value.
*
- * @param array|int $type The type to test
- * @param string|null $value The token value
+ * @param array|int $type The type to test
*
* @return bool
*/
- public function test($type, $value = null)
+ public function test($type, string $value = null)
{
return $this->type === $type && (null === $value || $this->value == $value);
}
diff --git a/src/Symfony/Component/ExpressionLanguage/TokenStream.php b/src/Symfony/Component/ExpressionLanguage/TokenStream.php
index 01277dc445529..a85e5853be2ca 100644
--- a/src/Symfony/Component/ExpressionLanguage/TokenStream.php
+++ b/src/Symfony/Component/ExpressionLanguage/TokenStream.php
@@ -59,10 +59,9 @@ public function next()
* Tests a token.
*
* @param array|int $type The type to test
- * @param string|null $value The token value
* @param string|null $message The syntax error message
*/
- public function expect($type, $value = null, $message = null)
+ public function expect($type, string $value = null, string $message = null)
{
$token = $this->current;
if (!$token->test($type, $value)) {
diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json
index 6843975675ef2..0742a3ef7b416 100644
--- a/src/Symfony/Component/ExpressionLanguage/composer.json
+++ b/src/Symfony/Component/ExpressionLanguage/composer.json
@@ -16,8 +16,8 @@
}
],
"require": {
- "php": "^7.1.3",
- "symfony/cache": "^3.4|^4.0|^5.0",
+ "php": "^7.2.5",
+ "symfony/cache": "^4.4|^5.0",
"symfony/service-contracts": "^1.1|^2"
},
"autoload": {
@@ -29,7 +29,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Filesystem/CHANGELOG.md b/src/Symfony/Component/Filesystem/CHANGELOG.md
index 0b633ef2a7726..4a0755bfe0a83 100644
--- a/src/Symfony/Component/Filesystem/CHANGELOG.md
+++ b/src/Symfony/Component/Filesystem/CHANGELOG.md
@@ -1,10 +1,16 @@
CHANGELOG
=========
+5.0.0
+-----
+
+ * `Filesystem::dumpFile()` and `appendToFile()` don't accept arrays anymore
+
4.4.0
-----
* support for passing a `null` value to `Filesystem::isAbsolutePath()` is deprecated and will be removed in 5.0
+ * `tempnam()` now accepts a third argument `$suffix`.
4.3.0
-----
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index a8c3bd4f7f24f..c8d3344fde3f3 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -31,14 +31,10 @@ class Filesystem
* If the target file is newer, it is overwritten only when the
* $overwriteNewerFiles option is set to true.
*
- * @param string $originFile The original filename
- * @param string $targetFile The target filename
- * @param bool $overwriteNewerFiles If true, target files newer than origin files are overwritten
- *
* @throws FileNotFoundException When originFile doesn't exist
* @throws IOException When copy fails
*/
- public function copy($originFile, $targetFile, $overwriteNewerFiles = false)
+ public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false)
{
$originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://');
if ($originIsLocal && !is_file($originFile)) {
@@ -87,11 +83,10 @@ public function copy($originFile, $targetFile, $overwriteNewerFiles = false)
* Creates a directory recursively.
*
* @param string|iterable $dirs The directory path
- * @param int $mode The directory mode
*
* @throws IOException On any directory creation failure
*/
- public function mkdir($dirs, $mode = 0777)
+ public function mkdir($dirs, int $mode = 0777)
{
foreach ($this->toIterable($dirs) as $dir) {
if (is_dir($dir)) {
@@ -143,7 +138,7 @@ public function exists($files)
*
* @throws IOException When touch fails
*/
- public function touch($files, $time = null, $atime = null)
+ public function touch($files, int $time = null, int $atime = null)
{
foreach ($this->toIterable($files) as $file) {
$touch = $time ? @touch($file, $time, $atime) : @touch($file);
@@ -196,7 +191,7 @@ public function remove($files)
*
* @throws IOException When the change fails
*/
- public function chmod($files, $mode, $umask = 0000, $recursive = false)
+ public function chmod($files, int $mode, int $umask = 0000, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if (true !== @chmod($file, $mode & ~$umask)) {
@@ -217,7 +212,7 @@ public function chmod($files, $mode, $umask = 0000, $recursive = false)
*
* @throws IOException When the change fails
*/
- public function chown($files, $user, $recursive = false)
+ public function chown($files, $user, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
@@ -244,7 +239,7 @@ public function chown($files, $user, $recursive = false)
*
* @throws IOException When the change fails
*/
- public function chgrp($files, $group, $recursive = false)
+ public function chgrp($files, $group, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
@@ -265,14 +260,10 @@ public function chgrp($files, $group, $recursive = false)
/**
* Renames a file or a directory.
*
- * @param string $origin The origin filename or directory
- * @param string $target The new filename or directory
- * @param bool $overwrite Whether to overwrite the target if it already exists
- *
* @throws IOException When target file or directory already exists
* @throws IOException When origin cannot be renamed
*/
- public function rename($origin, $target, $overwrite = false)
+ public function rename(string $origin, string $target, bool $overwrite = false)
{
// we check that target does not exist
if (!$overwrite && $this->isReadable($target)) {
@@ -310,13 +301,9 @@ private function isReadable(string $filename): bool
/**
* Creates a symbolic link or copy a directory.
*
- * @param string $originDir The origin directory path
- * @param string $targetDir The symbolic link name
- * @param bool $copyOnWindows Whether to copy files if on Windows
- *
* @throws IOException When symlink fails
*/
- public function symlink($originDir, $targetDir, $copyOnWindows = false)
+ public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false)
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$originDir = strtr($originDir, '/', '\\');
@@ -346,13 +333,12 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false)
/**
* Creates a hard link, or several hard links to a file.
*
- * @param string $originFile The original file
* @param string|string[] $targetFiles The target file(s)
*
* @throws FileNotFoundException When original file is missing or not a file
* @throws IOException When link fails, including if link already exists
*/
- public function hardlink($originFile, $targetFiles)
+ public function hardlink(string $originFile, $targetFiles)
{
if (!$this->exists($originFile)) {
throw new FileNotFoundException(null, 0, null, $originFile);
@@ -400,12 +386,9 @@ private function linkException(string $origin, string $target, string $linkType)
* - if $path does not exist, returns null
* - if $path exists, returns its absolute fully resolved final version
*
- * @param string $path A filesystem path
- * @param bool $canonicalize Whether or not to return a canonicalized path
- *
* @return string|null
*/
- public function readlink($path, $canonicalize = false)
+ public function readlink(string $path, bool $canonicalize = false)
{
if (!$canonicalize && !is_link($path)) {
return null;
@@ -433,12 +416,9 @@ public function readlink($path, $canonicalize = false)
/**
* Given an existing path, convert it to a path relative to a given starting path.
*
- * @param string $endPath Absolute path of target
- * @param string $startPath Absolute path where traversal begins
- *
* @return string Path of target relative to starting path
*/
- public function makePathRelative($endPath, $startPath)
+ public function makePathRelative(string $endPath, string $startPath)
{
if (!$this->isAbsolutePath($startPath)) {
throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath));
@@ -518,18 +498,16 @@ public function makePathRelative($endPath, $startPath)
* - existing files in the target directory will be overwritten, except if they are newer (see the `override` option)
* - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option)
*
- * @param string $originDir The origin directory
- * @param string $targetDir The target directory
- * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created
- * @param array $options An array of boolean options
- * Valid options are:
- * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false)
- * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false)
- * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
+ * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created
+ * @param array $options An array of boolean options
+ * Valid options are:
+ * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false)
+ * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false)
+ * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
*
* @throws IOException When file type is unknown
*/
- public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = [])
+ public function mirror(string $originDir, string $targetDir, \Traversable $iterator = null, array $options = [])
{
$targetDir = rtrim($targetDir, '/\\');
$originDir = rtrim($originDir, '/\\');
@@ -588,16 +566,10 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o
/**
* Returns whether the file path is an absolute path.
*
- * @param string $file A file path
- *
* @return bool
*/
- public function isAbsolutePath($file)
+ public function isAbsolutePath(string $file)
{
- if (null === $file) {
- @trigger_error(sprintf('Calling "%s()" with a null in the $file argument is deprecated since Symfony 4.4.', __METHOD__), E_USER_DEPRECATED);
- }
-
return strspn($file, '/\\', 0, 1)
|| (\strlen($file) > 3 && ctype_alpha($file[0])
&& ':' === $file[1]
@@ -610,18 +582,19 @@ public function isAbsolutePath($file)
/**
* Creates a temporary file with support for custom stream wrappers.
*
- * @param string $dir The directory where the temporary filename will be created
* @param string $prefix The prefix of the generated temporary filename
* Note: Windows uses only the first three characters of prefix
+ * @param string $suffix The suffix of the generated temporary filename
*
* @return string The new temporary filename (with path), or throw an exception on failure
*/
- public function tempnam($dir, $prefix)
+ public function tempnam(string $dir, string $prefix/*, string $suffix = ''*/)
{
+ $suffix = \func_num_args() > 2 ? func_get_arg(2) : '';
list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir);
// If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem
- if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) {
+ if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) {
$tmpFile = @tempnam($hierarchy, $prefix);
// If tempnam failed or no scheme return the filename otherwise prepend the scheme
@@ -639,7 +612,7 @@ public function tempnam($dir, $prefix)
// Loop until we create a valid temp file or have reached 10 attempts
for ($i = 0; $i < 10; ++$i) {
// Create a unique filename
- $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true);
+ $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true).$suffix;
// Use fopen instead of file_exists as some streams do not support stat
// Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability
@@ -662,15 +635,14 @@ public function tempnam($dir, $prefix)
/**
* Atomically dumps content into a file.
*
- * @param string $filename The file to be written to
- * @param string|resource $content The data to write into the file
+ * @param string|resource $content The data to write into the file
*
* @throws IOException if the file cannot be written to
*/
- public function dumpFile($filename, $content)
+ public function dumpFile(string $filename, $content)
{
if (\is_array($content)) {
- @trigger_error(sprintf('Calling "%s()" with an array in the $content argument is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
+ throw new \TypeError(sprintf('Argument 2 passed to %s() must be string or resource, array given.', __METHOD__));
}
$dir = \dirname($filename);
@@ -699,15 +671,14 @@ public function dumpFile($filename, $content)
/**
* Appends content to an existing file.
*
- * @param string $filename The file to which to append content
- * @param string|resource $content The content to append
+ * @param string|resource $content The content to append
*
* @throws IOException If the file is not writable
*/
- public function appendToFile($filename, $content)
+ public function appendToFile(string $filename, $content)
{
if (\is_array($content)) {
- @trigger_error(sprintf('Calling "%s()" with an array in the $content argument is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
+ throw new \TypeError(sprintf('Argument 2 passed to %s() must be string or resource, array given.', __METHOD__));
}
$dir = \dirname($filename);
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
index c85860be7f0f0..79b2f61d0c9c0 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
@@ -444,20 +444,6 @@ public function testChmodChangesFileMode()
$this->assertFilePermissions(400, $file);
}
- public function testChmodWithWrongModLeavesPreviousPermissionsUntouched()
- {
- $this->markAsSkippedIfChmodIsMissing();
-
- $dir = $this->workspace.\DIRECTORY_SEPARATOR.'file';
- touch($dir);
-
- $permissions = fileperms($dir);
-
- $this->filesystem->chmod($dir, 'Wrongmode');
-
- $this->assertSame($permissions, fileperms($dir));
- }
-
public function testChmodRecursive()
{
$this->markAsSkippedIfChmodIsMissing();
@@ -535,7 +521,7 @@ public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive()
$this->assertFilePermissions(753, $subdirectory);
}
- public function testChown()
+ public function testChownByName()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -548,7 +534,20 @@ public function testChown()
$this->assertSame($owner, $this->getFileOwner($dir));
}
- public function testChownRecursive()
+ public function testChownById()
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
+ mkdir($dir);
+
+ $ownerId = $this->getFileOwnerId($dir);
+ $this->filesystem->chown($dir, $ownerId);
+
+ $this->assertSame($ownerId, $this->getFileOwnerId($dir));
+ }
+
+ public function testChownRecursiveByName()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -563,6 +562,21 @@ public function testChownRecursive()
$this->assertSame($owner, $this->getFileOwner($file));
}
+ public function testChownRecursiveById()
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
+ mkdir($dir);
+ $file = $dir.\DIRECTORY_SEPARATOR.'file';
+ touch($file);
+
+ $ownerId = $this->getFileOwnerId($dir);
+ $this->filesystem->chown($dir, $ownerId, true);
+
+ $this->assertSame($ownerId, $this->getFileOwnerId($file));
+ }
+
public function testChownSymlink()
{
$this->markAsSkippedIfSymlinkIsMissing();
@@ -638,7 +652,7 @@ public function testChownFail()
$this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999));
}
- public function testChgrp()
+ public function testChgrpByName()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -651,6 +665,19 @@ public function testChgrp()
$this->assertSame($group, $this->getFileGroup($dir));
}
+ public function testChgrpById()
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
+ mkdir($dir);
+
+ $groupId = $this->getFileGroupId($dir);
+ $this->filesystem->chgrp($dir, $groupId);
+
+ $this->assertSame($groupId, $this->getFileGroupId($dir));
+ }
+
public function testChgrpRecursive()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -666,7 +693,7 @@ public function testChgrpRecursive()
$this->assertSame($group, $this->getFileGroup($file));
}
- public function testChgrpSymlink()
+ public function testChgrpSymlinkByName()
{
$this->markAsSkippedIfSymlinkIsMissing();
@@ -683,6 +710,23 @@ public function testChgrpSymlink()
$this->assertSame($group, $this->getFileGroup($link));
}
+ public function testChgrpSymlinkById()
+ {
+ $this->markAsSkippedIfSymlinkIsMissing();
+
+ $file = $this->workspace.\DIRECTORY_SEPARATOR.'file';
+ $link = $this->workspace.\DIRECTORY_SEPARATOR.'link';
+
+ touch($file);
+
+ $this->filesystem->symlink($file, $link);
+
+ $groupId = $this->getFileGroupId($link);
+ $this->filesystem->chgrp($link, $groupId);
+
+ $this->assertSame($groupId, $this->getFileGroupId($link));
+ }
+
public function testChgrpLink()
{
$this->markAsSkippedIfLinkIsMissing();
@@ -1368,15 +1412,6 @@ public function providePathsForIsAbsolutePath()
];
}
- /**
- * @group legacy
- * @expectedDeprecation Calling "Symfony\Component\Filesystem\Filesystem::isAbsolutePath()" with a null in the $file argument is deprecated since Symfony 4.4.
- */
- public function testIsAbsolutePathWithNull()
- {
- $this->assertFalse($this->filesystem->isAbsolutePath(null));
- }
-
public function testTempnam()
{
$dirname = $this->workspace;
@@ -1474,6 +1509,22 @@ public function testTempnamOnUnwritableFallsBackToSysTmp()
@unlink($filename);
}
+ public function testTempnamWithSuffix()
+ {
+ $dirname = $this->workspace;
+ $filename = $this->filesystem->tempnam($dirname, 'foo', '.bar');
+ $this->assertStringEndsWith('.bar', $filename);
+ $this->assertFileExists($filename);
+ }
+
+ public function testTempnamWithSuffix0()
+ {
+ $dirname = $this->workspace;
+ $filename = $this->filesystem->tempnam($dirname, 'foo', '0');
+ $this->assertStringEndsWith('0', $filename);
+ $this->assertFileExists($filename);
+ }
+
public function testDumpFile()
{
$filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt';
@@ -1494,20 +1545,6 @@ public function testDumpFile()
}
}
- /**
- * @group legacy
- * @expectedDeprecation Calling "Symfony\Component\Filesystem\Filesystem::dumpFile()" with an array in the $content argument is deprecated since Symfony 4.3.
- */
- public function testDumpFileWithArray()
- {
- $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt';
-
- $this->filesystem->dumpFile($filename, ['bar']);
-
- $this->assertFileExists($filename);
- $this->assertStringEqualsFile($filename, 'bar');
- }
-
public function testDumpFileWithResource()
{
$filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt';
@@ -1580,33 +1617,6 @@ public function testAppendToFile()
}
}
- /**
- * @group legacy
- * @expectedDeprecation Calling "Symfony\Component\Filesystem\Filesystem::appendToFile()" with an array in the $content argument is deprecated since Symfony 4.3.
- */
- public function testAppendToFileWithArray()
- {
- $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'bar.txt';
-
- // skip mode check on Windows
- if ('\\' !== \DIRECTORY_SEPARATOR) {
- $oldMask = umask(0002);
- }
-
- $this->filesystem->dumpFile($filename, 'foo');
-
- $this->filesystem->appendToFile($filename, ['bar']);
-
- $this->assertFileExists($filename);
- $this->assertStringEqualsFile($filename, 'foobar');
-
- // skip mode check on Windows
- if ('\\' !== \DIRECTORY_SEPARATOR) {
- $this->assertFilePermissions(664, $filename);
- umask($oldMask);
- }
- }
-
public function testAppendToFileWithResource()
{
$filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'bar.txt';
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php
index 08afefbb24eff..396a3ab2e8634 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php
@@ -105,21 +105,36 @@ protected function assertFilePermissions($expectedFilePerms, $filePath)
);
}
+ protected function getFileOwnerId($filepath)
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $infos = stat($filepath);
+
+ return $infos['uid'];
+ }
+
protected function getFileOwner($filepath)
{
$this->markAsSkippedIfPosixIsMissing();
+ return ($datas = posix_getpwuid($this->getFileOwnerId($filepath))) ? $datas['name'] : null;
+ }
+
+ protected function getFileGroupId($filepath)
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
$infos = stat($filepath);
- return ($datas = posix_getpwuid($infos['uid'])) ? $datas['name'] : null;
+ return $infos['gid'];
}
protected function getFileGroup($filepath)
{
$this->markAsSkippedIfPosixIsMissing();
- $infos = stat($filepath);
- if ($datas = posix_getgrgid($infos['gid'])) {
+ if ($datas = posix_getgrgid($this->getFileGroupId($filepath))) {
return $datas['name'];
}
diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json
index 9e0373d2f6662..9b5bfa9d266e4 100644
--- a/src/Symfony/Component/Filesystem/composer.json
+++ b/src/Symfony/Component/Filesystem/composer.json
@@ -16,7 +16,7 @@
}
],
"require": {
- "php": "^7.1.3",
+ "php": "^7.2.5",
"symfony/polyfill-ctype": "~1.8"
},
"autoload": {
@@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Finder/CHANGELOG.md b/src/Symfony/Component/Finder/CHANGELOG.md
index 2045184e83331..33f5bd589eb7b 100644
--- a/src/Symfony/Component/Finder/CHANGELOG.md
+++ b/src/Symfony/Component/Finder/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+5.0.0
+-----
+
+ * added `$useNaturalSort` argument to `Finder::sortByName()`
+
4.3.0
-----
diff --git a/src/Symfony/Component/Finder/Comparator/Comparator.php b/src/Symfony/Component/Finder/Comparator/Comparator.php
index 6aee21cf09290..cfe3965fac98f 100644
--- a/src/Symfony/Component/Finder/Comparator/Comparator.php
+++ b/src/Symfony/Component/Finder/Comparator/Comparator.php
@@ -31,12 +31,7 @@ public function getTarget()
return $this->target;
}
- /**
- * Sets the target value.
- *
- * @param string $target The target value
- */
- public function setTarget($target)
+ public function setTarget(string $target)
{
$this->target = $target;
}
@@ -54,13 +49,11 @@ public function getOperator()
/**
* Sets the comparison operator.
*
- * @param string $operator A valid operator
- *
* @throws \InvalidArgumentException
*/
- public function setOperator($operator)
+ public function setOperator(string $operator)
{
- if (!$operator) {
+ if ('' === $operator) {
$operator = '==';
}
diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php
index e1f27a8e2ef74..e1bcea35d2656 100644
--- a/src/Symfony/Component/Finder/Finder.php
+++ b/src/Symfony/Component/Finder/Finder.php
@@ -336,13 +336,11 @@ public function exclude($dirs)
*
* This option is enabled by default.
*
- * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
- *
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
- public function ignoreDotFiles($ignoreDotFiles)
+ public function ignoreDotFiles(bool $ignoreDotFiles)
{
if ($ignoreDotFiles) {
$this->ignore |= static::IGNORE_DOT_FILES;
@@ -358,13 +356,11 @@ public function ignoreDotFiles($ignoreDotFiles)
*
* This option is enabled by default.
*
- * @param bool $ignoreVCS Whether to exclude VCS files or not
- *
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
- public function ignoreVCS($ignoreVCS)
+ public function ignoreVCS(bool $ignoreVCS)
{
if ($ignoreVCS) {
$this->ignore |= static::IGNORE_VCS_FILES;
@@ -432,19 +428,12 @@ public function sort(\Closure $closure)
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
- * @param bool $useNaturalSort Whether to use natural sort or not, disabled by default
- *
* @return $this
*
* @see SortableIterator
*/
- public function sortByName(/* bool $useNaturalSort = false */)
+ public function sortByName(bool $useNaturalSort = false)
{
- if (\func_num_args() < 1 && __CLASS__ !== static::class && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
- @trigger_error(sprintf('The "%s()" method will have a new "bool $useNaturalSort = false" argument in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- }
- $useNaturalSort = 0 < \func_num_args() && func_get_arg(0);
-
$this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
return $this;
@@ -568,13 +557,11 @@ public function followLinks()
*
* By default, scanning unreadable directories content throws an AccessDeniedException.
*
- * @param bool $ignore
- *
* @return $this
*/
- public function ignoreUnreadableDirs($ignore = true)
+ public function ignoreUnreadableDirs(bool $ignore = true)
{
- $this->ignoreUnreadableDirs = (bool) $ignore;
+ $this->ignoreUnreadableDirs = $ignore;
return $this;
}
@@ -644,13 +631,11 @@ public function getIterator()
*
* The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
*
- * @param iterable $iterator
- *
* @return $this
*
* @throws \InvalidArgumentException when the given argument is not iterable
*/
- public function append($iterator)
+ public function append(iterable $iterator)
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
diff --git a/src/Symfony/Component/Finder/Glob.php b/src/Symfony/Component/Finder/Glob.php
index ea76d51ae0f9f..8447932e57a72 100644
--- a/src/Symfony/Component/Finder/Glob.php
+++ b/src/Symfony/Component/Finder/Glob.php
@@ -38,14 +38,9 @@ class Glob
/**
* Returns a regexp which is the equivalent of the glob pattern.
*
- * @param string $glob The glob pattern
- * @param bool $strictLeadingDot
- * @param bool $strictWildcardSlash
- * @param string $delimiter Optional delimiter
- *
- * @return string regex The regexp
+ * @return string
*/
- public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#')
+ public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#')
{
$firstByte = true;
$escaping = false;
diff --git a/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php b/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php
index 81594b8774048..b26a368483261 100644
--- a/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php
+++ b/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php
@@ -51,7 +51,7 @@ public function accept()
*
* @return string regexp corresponding to a given string or regexp
*/
- protected function toRegex($str)
+ protected function toRegex(string $str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
diff --git a/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php b/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php
index e168cd8ffa798..dedd1ca55e878 100644
--- a/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php
+++ b/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php
@@ -40,7 +40,7 @@ public function accept()
*
* @return string regexp corresponding to a given glob or regexp
*/
- protected function toRegex($str)
+ protected function toRegex(string $str)
{
return $this->isRegex($str) ? $str : Glob::toRegex($str);
}
diff --git a/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php b/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php
index 18b082ec0b10b..78a34abef8877 100644
--- a/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php
+++ b/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php
@@ -23,8 +23,8 @@ abstract class MultiplePcreFilterIterator extends \FilterIterator
/**
* @param \Iterator $iterator The Iterator to filter
- * @param array $matchPatterns An array of patterns that need to match
- * @param array $noMatchPatterns An array of patterns that need to not match
+ * @param string[] $matchPatterns An array of patterns that need to match
+ * @param string[] $noMatchPatterns An array of patterns that need to not match
*/
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
@@ -46,11 +46,9 @@ public function __construct(\Iterator $iterator, array $matchPatterns, array $no
* Such case can be handled by child classes before calling the method if they want to
* apply a different behavior.
*
- * @param string $string The string to be matched against filters
- *
* @return bool
*/
- protected function isAccepted($string)
+ protected function isAccepted(string $string)
{
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
@@ -77,11 +75,9 @@ protected function isAccepted($string)
/**
* Checks whether the string is a regex.
*
- * @param string $str
- *
- * @return bool Whether the given string is a regex
+ * @return bool
*/
- protected function isRegex($str)
+ protected function isRegex(string $str)
{
if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) {
$start = substr($m[1], 0, 1);
@@ -104,9 +100,7 @@ protected function isRegex($str)
/**
* Converts string into regexp.
*
- * @param string $str Pattern
- *
- * @return string regexp corresponding to a given string
+ * @return string
*/
- abstract protected function toRegex($str);
+ abstract protected function toRegex(string $str);
}
diff --git a/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php b/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php
index 3fda557be366b..67b71f46d7724 100644
--- a/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php
+++ b/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php
@@ -49,7 +49,7 @@ public function accept()
*
* @return string regexp corresponding to a given string or regexp
*/
- protected function toRegex($str)
+ protected function toRegex(string $str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
diff --git a/src/Symfony/Component/Finder/Iterator/SortableIterator.php b/src/Symfony/Component/Finder/Iterator/SortableIterator.php
index 8f0090c746dfe..2aca397e8807b 100644
--- a/src/Symfony/Component/Finder/Iterator/SortableIterator.php
+++ b/src/Symfony/Component/Finder/Iterator/SortableIterator.php
@@ -41,15 +41,15 @@ public function __construct(\Traversable $iterator, $sort, bool $reverseOrder =
$order = $reverseOrder ? -1 : 1;
if (self::SORT_BY_NAME === $sort) {
- $this->sort = static function ($a, $b) use ($order) {
+ $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_NAME_NATURAL === $sort) {
- $this->sort = static function ($a, $b) use ($order) {
+ $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_TYPE === $sort) {
- $this->sort = static function ($a, $b) use ($order) {
+ $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
if ($a->isDir() && $b->isFile()) {
return -$order;
} elseif ($a->isFile() && $b->isDir()) {
@@ -59,21 +59,21 @@ public function __construct(\Traversable $iterator, $sort, bool $reverseOrder =
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
- $this->sort = static function ($a, $b) use ($order) {
+ $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getATime() - $b->getATime());
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
- $this->sort = static function ($a, $b) use ($order) {
+ $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getCTime() - $b->getCTime());
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
- $this->sort = static function ($a, $b) use ($order) {
+ $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getMTime() - $b->getMTime());
};
} elseif (self::SORT_BY_NONE === $sort) {
$this->sort = $order;
} elseif (\is_callable($sort)) {
- $this->sort = $reverseOrder ? static function ($a, $b) use ($sort) { return -$sort($a, $b); } : $sort;
+ $this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : $sort;
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
}
diff --git a/src/Symfony/Component/Finder/Tests/ClassThatInheritFinder.php b/src/Symfony/Component/Finder/Tests/ClassThatInheritFinder.php
deleted file mode 100644
index f937f6247c446..0000000000000
--- a/src/Symfony/Component/Finder/Tests/ClassThatInheritFinder.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Finder\Tests;
-
-use Symfony\Component\Finder\Finder;
-
-class ClassThatInheritFinder extends Finder
-{
- /**
- * @return $this
- */
- public function sortByName()
- {
- parent::sortByName();
- }
-}
diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php
index 16a5b2a2f211d..e26aef16663f9 100644
--- a/src/Symfony/Component/Finder/Tests/FinderTest.php
+++ b/src/Symfony/Component/Finder/Tests/FinderTest.php
@@ -873,7 +873,6 @@ public function testIn()
$expected = [
self::$tmpDir.\DIRECTORY_SEPARATOR.'test.php',
- __DIR__.\DIRECTORY_SEPARATOR.'ClassThatInheritFinder.php',
__DIR__.\DIRECTORY_SEPARATOR.'GitignoreTest.php',
__DIR__.\DIRECTORY_SEPARATOR.'FinderTest.php',
__DIR__.\DIRECTORY_SEPARATOR.'GlobTest.php',
@@ -1421,16 +1420,6 @@ public function testIgnoredAccessDeniedException()
}
}
- /**
- * @group legacy
- * @expectedDeprecation The "Symfony\Component\Finder\Finder::sortByName()" method will have a new "bool $useNaturalSort = false" argument in version 5.0, not defining it is deprecated since Symfony 4.2.
- */
- public function testInheritedClassCallSortByNameWithNoArguments()
- {
- $finderChild = new ClassThatInheritFinder();
- $finderChild->sortByName();
- }
-
protected function buildFinder()
{
return Finder::create();
diff --git a/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php
index 15e069b9f81c1..69f020e9ec0e5 100644
--- a/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php
+++ b/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php
@@ -59,12 +59,12 @@ public function accept()
throw new \BadFunctionCallException('Not implemented');
}
- public function isRegex($str): bool
+ public function isRegex(string $str): bool
{
return parent::isRegex($str);
}
- public function toRegex($str): string
+ public function toRegex(string $str): string
{
throw new \BadFunctionCallException('Not implemented');
}
diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json
index 0b1408c0dfd41..37f84c7b3aa64 100644
--- a/src/Symfony/Component/Finder/composer.json
+++ b/src/Symfony/Component/Finder/composer.json
@@ -16,7 +16,7 @@
}
],
"require": {
- "php": "^7.1.3"
+ "php": "^7.2.5"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Finder\\": "" },
@@ -27,7 +27,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php
index bda7ef129ee3e..02fcea9e53e1b 100644
--- a/src/Symfony/Component/Form/AbstractExtension.php
+++ b/src/Symfony/Component/Form/AbstractExtension.php
@@ -50,7 +50,7 @@ abstract class AbstractExtension implements FormExtensionInterface
/**
* {@inheritdoc}
*/
- public function getType($name)
+ public function getType(string $name)
{
if (null === $this->types) {
$this->initTypes();
@@ -66,7 +66,7 @@ public function getType($name)
/**
* {@inheritdoc}
*/
- public function hasType($name)
+ public function hasType(string $name)
{
if (null === $this->types) {
$this->initTypes();
@@ -78,7 +78,7 @@ public function hasType($name)
/**
* {@inheritdoc}
*/
- public function getTypeExtensions($name)
+ public function getTypeExtensions(string $name)
{
if (null === $this->typeExtensions) {
$this->initTypeExtensions();
@@ -92,7 +92,7 @@ public function getTypeExtensions($name)
/**
* {@inheritdoc}
*/
- public function hasTypeExtensions($name)
+ public function hasTypeExtensions(string $name)
{
if (null === $this->typeExtensions) {
$this->initTypeExtensions();
@@ -176,19 +176,7 @@ private function initTypeExtensions()
throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface');
}
- if (method_exists($extension, 'getExtendedTypes')) {
- $extendedTypes = [];
-
- foreach ($extension::getExtendedTypes() as $extendedType) {
- $extendedTypes[] = $extendedType;
- }
- } else {
- @trigger_error(sprintf('Not implementing the "%s::getExtendedTypes()" method in "%s" is deprecated since Symfony 4.2.', FormTypeExtensionInterface::class, \get_class($extension)), E_USER_DEPRECATED);
-
- $extendedTypes = [$extension->getExtendedType()];
- }
-
- foreach ($extendedTypes as $extendedType) {
+ foreach ($extension::getExtendedTypes() as $extendedType) {
$this->typeExtensions[$extendedType][] = $extension;
}
}
diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php
index cd2e764b8ec4a..370f74e3a896a 100644
--- a/src/Symfony/Component/Form/AbstractRendererEngine.php
+++ b/src/Symfony/Component/Form/AbstractRendererEngine.php
@@ -44,7 +44,7 @@ public function __construct(array $defaultThemes = [])
/**
* {@inheritdoc}
*/
- public function setTheme(FormView $view, $themes, $useDefaultThemes = true)
+ public function setTheme(FormView $view, $themes, bool $useDefaultThemes = true)
{
$cacheKey = $view->vars[self::CACHE_KEY_VAR];
@@ -61,7 +61,7 @@ public function setTheme(FormView $view, $themes, $useDefaultThemes = true)
/**
* {@inheritdoc}
*/
- public function getResourceForBlockName(FormView $view, $blockName)
+ public function getResourceForBlockName(FormView $view, string $blockName)
{
$cacheKey = $view->vars[self::CACHE_KEY_VAR];
@@ -75,7 +75,7 @@ public function getResourceForBlockName(FormView $view, $blockName)
/**
* {@inheritdoc}
*/
- public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, $hierarchyLevel)
+ public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, int $hierarchyLevel)
{
$cacheKey = $view->vars[self::CACHE_KEY_VAR];
$blockName = $blockNameHierarchy[$hierarchyLevel];
@@ -90,7 +90,7 @@ public function getResourceForBlockNameHierarchy(FormView $view, array $blockNam
/**
* {@inheritdoc}
*/
- public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, $hierarchyLevel)
+ public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, int $hierarchyLevel)
{
$cacheKey = $view->vars[self::CACHE_KEY_VAR];
$blockName = $blockNameHierarchy[$hierarchyLevel];
@@ -114,20 +114,16 @@ public function getResourceHierarchyLevel(FormView $view, array $blockNameHierar
*
* @see getResourceForBlock()
*
- * @param string $cacheKey The cache key of the form view
- * @param FormView $view The form view for finding the applying themes
- * @param string $blockName The name of the block to load
- *
* @return bool True if the resource could be loaded, false otherwise
*/
- abstract protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName);
+ abstract protected function loadResourceForBlockName(string $cacheKey, FormView $view, string $blockName);
/**
* Loads the cache with the resource for a specific level of a block hierarchy.
*
* @see getResourceForBlockHierarchy()
*/
- private function loadResourceForBlockNameHierarchy(string $cacheKey, FormView $view, array $blockNameHierarchy, $hierarchyLevel): bool
+ private function loadResourceForBlockNameHierarchy(string $cacheKey, FormView $view, array $blockNameHierarchy, int $hierarchyLevel): bool
{
$blockName = $blockNameHierarchy[$hierarchyLevel];
diff --git a/src/Symfony/Component/Form/AbstractTypeExtension.php b/src/Symfony/Component/Form/AbstractTypeExtension.php
index 0388f7c012919..9d369bf294e96 100644
--- a/src/Symfony/Component/Form/AbstractTypeExtension.php
+++ b/src/Symfony/Component/Form/AbstractTypeExtension.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Form;
-use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
@@ -46,22 +45,4 @@ public function finishView(FormView $view, FormInterface $form, array $options)
public function configureOptions(OptionsResolver $resolver)
{
}
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.2, use getExtendedTypes() instead.
- */
- public function getExtendedType()
- {
- if (!method_exists($this, 'getExtendedTypes')) {
- throw new LogicException(sprintf('You need to implement the static getExtendedTypes() method when implementing the %s in %s.', FormTypeExtensionInterface::class, static::class));
- }
-
- @trigger_error(sprintf('The %s::getExtendedType() method is deprecated since Symfony 4.2 and will be removed in 5.0. Use getExtendedTypes() instead.', static::class), E_USER_DEPRECATED);
-
- foreach (static::getExtendedTypes() as $extendedType) {
- return $extendedType;
- }
- }
}
diff --git a/src/Symfony/Component/Form/Button.php b/src/Symfony/Component/Form/Button.php
index 50acb8db0c6e8..4188ef3e2c731 100644
--- a/src/Symfony/Component/Form/Button.php
+++ b/src/Symfony/Component/Form/Button.php
@@ -128,7 +128,7 @@ public function getParent()
*
* @throws BadMethodCallException
*/
- public function add($child, $type = null, array $options = [])
+ public function add($child, string $type = null, array $options = [])
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -138,11 +138,9 @@ public function add($child, $type = null, array $options = [])
*
* This method should not be invoked.
*
- * @param string $name
- *
* @throws BadMethodCallException
*/
- public function get($name)
+ public function get(string $name)
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -150,11 +148,9 @@ public function get($name)
/**
* Unsupported method.
*
- * @param string $name
- *
* @return bool Always returns false
*/
- public function has($name)
+ public function has(string $name)
{
return false;
}
@@ -164,11 +160,9 @@ public function has($name)
*
* This method should not be invoked.
*
- * @param string $name
- *
* @throws BadMethodCallException
*/
- public function remove($name)
+ public function remove(string $name)
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -184,7 +178,7 @@ public function all()
/**
* {@inheritdoc}
*/
- public function getErrors($deep = false, $flatten = true)
+ public function getErrors(bool $deep = false, bool $flatten = true)
{
return new FormErrorIterator($this, []);
}
@@ -378,7 +372,7 @@ public function handleRequest($request = null)
*
* @throws Exception\AlreadySubmittedException if the button has already been submitted
*/
- public function submit($submittedData, $clearMissing = true)
+ public function submit($submittedData, bool $clearMissing = true)
{
if ($this->submitted) {
throw new AlreadySubmittedException('A form can only be submitted once');
diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php
index 2af869c66d0d4..87adc69475dc5 100644
--- a/src/Symfony/Component/Form/ButtonBuilder.php
+++ b/src/Symfony/Component/Form/ButtonBuilder.php
@@ -61,17 +61,7 @@ public function __construct(?string $name, array $options = [])
$this->name = $name;
$this->options = $options;
- if (preg_match('/^([^a-zA-Z0-9_].*)?(.*[^a-zA-Z0-9_\-:].*)?$/D', $name, $matches)) {
- if (isset($matches[1])) {
- @trigger_error(sprintf('Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated since Symfony 4.3 and will throw an exception in 5.0 ("%s" given).', $name), E_USER_DEPRECATED);
- }
- if (isset($matches[2])) {
- @trigger_error(sprintf('Using names for buttons that do not contain only letters, digits, underscores ("_"), hyphens ("-") and colons (":") ("%s" given) is deprecated since Symfony 4.3 and will throw an exception in 5.0.', $name), E_USER_DEPRECATED);
- }
- }
-
- // to be added in 5.0
- // FormConfigBuilder::validateName($name);
+ FormConfigBuilder::validateName($name);
}
/**
@@ -81,7 +71,7 @@ public function __construct(?string $name, array $options = [])
*
* @throws BadMethodCallException
*/
- public function add($child, $type = null, array $options = [])
+ public function add($child, string $type = null, array $options = [])
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -93,7 +83,7 @@ public function add($child, $type = null, array $options = [])
*
* @throws BadMethodCallException
*/
- public function create($name, $type = null, array $options = [])
+ public function create(string $name, string $type = null, array $options = [])
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -103,11 +93,9 @@ public function create($name, $type = null, array $options = [])
*
* This method should not be invoked.
*
- * @param string $name
- *
* @throws BadMethodCallException
*/
- public function get($name)
+ public function get(string $name)
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -117,11 +105,9 @@ public function get($name)
*
* This method should not be invoked.
*
- * @param string $name
- *
* @throws BadMethodCallException
*/
- public function remove($name)
+ public function remove(string $name)
{
throw new BadMethodCallException('Buttons cannot have children.');
}
@@ -129,11 +115,9 @@ public function remove($name)
/**
* Unsupported method.
*
- * @param string $name
- *
* @return bool Always returns false
*/
- public function has($name)
+ public function has(string $name)
{
return false;
}
@@ -163,13 +147,9 @@ public function getForm()
*
* This method should not be invoked.
*
- * @param string $eventName
- * @param callable $listener
- * @param int $priority
- *
* @throws BadMethodCallException
*/
- public function addEventListener($eventName, $listener, $priority = 0)
+ public function addEventListener(string $eventName, callable $listener, int $priority = 0)
{
throw new BadMethodCallException('Buttons do not support event listeners.');
}
@@ -191,11 +171,9 @@ public function addEventSubscriber(EventSubscriberInterface $subscriber)
*
* This method should not be invoked.
*
- * @param bool $forcePrepend
- *
* @throws BadMethodCallException
*/
- public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false)
+ public function addViewTransformer(DataTransformerInterface $viewTransformer, bool $forcePrepend = false)
{
throw new BadMethodCallException('Buttons do not support data transformers.');
}
@@ -217,11 +195,9 @@ public function resetViewTransformers()
*
* This method should not be invoked.
*
- * @param bool $forceAppend
- *
* @throws BadMethodCallException
*/
- public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false)
+ public function addModelTransformer(DataTransformerInterface $modelTransformer, bool $forceAppend = false)
{
throw new BadMethodCallException('Buttons do not support data transformers.');
}
@@ -241,7 +217,7 @@ public function resetModelTransformers()
/**
* {@inheritdoc}
*/
- public function setAttribute($name, $value)
+ public function setAttribute(string $name, $value)
{
$this->attributes[$name] = $value;
@@ -273,11 +249,9 @@ public function setDataMapper(DataMapperInterface $dataMapper = null)
/**
* Set whether the button is disabled.
*
- * @param bool $disabled Whether the button is disabled
- *
* @return $this
*/
- public function setDisabled($disabled)
+ public function setDisabled(bool $disabled)
{
$this->disabled = $disabled;
@@ -303,11 +277,9 @@ public function setEmptyData($emptyData)
*
* This method should not be invoked.
*
- * @param bool $errorBubbling
- *
* @throws BadMethodCallException
*/
- public function setErrorBubbling($errorBubbling)
+ public function setErrorBubbling(bool $errorBubbling)
{
throw new BadMethodCallException('Buttons do not support error bubbling.');
}
@@ -317,11 +289,9 @@ public function setErrorBubbling($errorBubbling)
*
* This method should not be invoked.
*
- * @param bool $required
- *
* @throws BadMethodCallException
*/
- public function setRequired($required)
+ public function setRequired(bool $required)
{
throw new BadMethodCallException('Buttons cannot be required.');
}
@@ -345,11 +315,9 @@ public function setPropertyPath($propertyPath)
*
* This method should not be invoked.
*
- * @param bool $mapped
- *
* @throws BadMethodCallException
*/
- public function setMapped($mapped)
+ public function setMapped(bool $mapped)
{
throw new BadMethodCallException('Buttons do not support data mapping.');
}
@@ -359,11 +327,9 @@ public function setMapped($mapped)
*
* This method should not be invoked.
*
- * @param bool $byReference
- *
* @throws BadMethodCallException
*/
- public function setByReference($byReference)
+ public function setByReference(bool $byReference)
{
throw new BadMethodCallException('Buttons do not support data mapping.');
}
@@ -373,11 +339,9 @@ public function setByReference($byReference)
*
* This method should not be invoked.
*
- * @param bool $compound
- *
* @throws BadMethodCallException
*/
- public function setCompound($compound)
+ public function setCompound(bool $compound)
{
throw new BadMethodCallException('Buttons cannot be compound.');
}
@@ -413,11 +377,9 @@ public function setData($data)
*
* This method should not be invoked.
*
- * @param bool $locked
- *
* @throws BadMethodCallException
*/
- public function setDataLocked($locked)
+ public function setDataLocked(bool $locked)
{
throw new BadMethodCallException('Buttons do not support data locking.');
}
@@ -437,11 +399,9 @@ public function setFormFactory(FormFactoryInterface $formFactory)
/**
* Unsupported method.
*
- * @param string $action
- *
* @throws BadMethodCallException
*/
- public function setAction($action)
+ public function setAction(string $action)
{
throw new BadMethodCallException('Buttons do not support actions.');
}
@@ -449,11 +409,9 @@ public function setAction($action)
/**
* Unsupported method.
*
- * @param string $method
- *
* @throws BadMethodCallException
*/
- public function setMethod($method)
+ public function setMethod(string $method)
{
throw new BadMethodCallException('Buttons do not support methods.');
}
@@ -471,13 +429,11 @@ public function setRequestHandler(RequestHandlerInterface $requestHandler)
/**
* Unsupported method.
*
- * @param bool $initialize
- *
* @return $this
*
* @throws BadMethodCallException
*/
- public function setAutoInitialize($initialize)
+ public function setAutoInitialize(bool $initialize)
{
if (true === $initialize) {
throw new BadMethodCallException('Buttons do not support automatic initialization.');
@@ -489,11 +445,9 @@ public function setAutoInitialize($initialize)
/**
* Unsupported method.
*
- * @param bool $inheritData
- *
* @throws BadMethodCallException
*/
- public function setInheritData($inheritData)
+ public function setInheritData(bool $inheritData)
{
throw new BadMethodCallException('Buttons do not support data inheritance.');
}
@@ -512,6 +466,16 @@ public function getFormConfig()
return $config;
}
+ /**
+ * Unsupported method.
+ *
+ * @throws BadMethodCallException
+ */
+ public function setIsEmptyCallback(?callable $isEmptyCallback)
+ {
+ throw new BadMethodCallException('Buttons do not support "is empty" callback.');
+ }
+
/**
* Unsupported method.
*/
@@ -655,11 +619,9 @@ public function getAttributes()
/**
* Returns whether the attribute with the given name exists.
*
- * @param string $name The attribute name
- *
* @return bool Whether the attribute exists
*/
- public function hasAttribute($name)
+ public function hasAttribute(string $name)
{
return \array_key_exists($name, $this->attributes);
}
@@ -667,12 +629,11 @@ public function hasAttribute($name)
/**
* Returns the value of the given attribute.
*
- * @param string $name The attribute name
- * @param mixed $default The value returned if the attribute does not exist
+ * @param mixed $default The value returned if the attribute does not exist
*
* @return mixed The attribute value
*/
- public function getAttribute($name, $default = null)
+ public function getAttribute(string $name, $default = null)
{
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
@@ -768,11 +729,9 @@ public function getOptions()
/**
* Returns whether a specific option exists.
*
- * @param string $name The option name,
- *
* @return bool Whether the option exists
*/
- public function hasOption($name)
+ public function hasOption(string $name)
{
return \array_key_exists($name, $this->options);
}
@@ -780,16 +739,25 @@ public function hasOption($name)
/**
* Returns the value of a specific option.
*
- * @param string $name The option name
- * @param mixed $default The value returned if the option does not exist
+ * @param mixed $default The value returned if the option does not exist
*
* @return mixed The option value
*/
- public function getOption($name, $default = null)
+ public function getOption(string $name, $default = null)
{
return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
}
+ /**
+ * Unsupported method.
+ *
+ * @throws BadMethodCallException
+ */
+ public function getIsEmptyCallback(): ?callable
+ {
+ throw new BadMethodCallException('Buttons do not support "is empty" callback.');
+ }
+
/**
* Unsupported method.
*
diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md
index 6d35f6fe5fa07..95a3d435b23c0 100644
--- a/src/Symfony/Component/Form/CHANGELOG.md
+++ b/src/Symfony/Component/Form/CHANGELOG.md
@@ -1,6 +1,39 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * Added a `ChoiceList` facade to leverage explicit choice list caching based on options
+ * Added an `AbstractChoiceLoader` to simplify implementations and handle global optimizations
+ * The `view_timezone` option defaults to the `model_timezone` if no `reference_date` is configured.
+ * Added default `inputmode` attribute to Search, Email and Tel form types.
+ * Implementing the `FormConfigInterface` without implementing the `getIsEmptyCallback()` method
+ is deprecated. The method will be added to the interface in 6.0.
+ * Implementing the `FormConfigBuilderInterface` without implementing the `setIsEmptyCallback()` method
+ is deprecated. The method will be added to the interface in 6.0.
+
+5.0.0
+-----
+
+ * Removed support for using different values for the "model_timezone" and "view_timezone" options of the `TimeType`
+ without configuring a reference date.
+ * Removed the `scale` option of the `IntegerType`.
+ * Using the `date_format`, `date_widget`, and `time_widget` options of the `DateTimeType` when the `widget` option is
+ set to `single_text` is not supported anymore.
+ * The `format` option of `DateType` and `DateTimeType` cannot be used when the `html5` option is enabled.
+ * Using names for buttons that do not start with a letter, a digit, or an underscore throw an exception
+ * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons throw an exception.
+ * removed the `ChoiceLoaderInterface` implementation in `CountryType`, `LanguageType`, `LocaleType` and `CurrencyType`
+ * removed `getExtendedType()` method of the `FormTypeExtensionInterface`
+ * added static `getExtendedTypes()` method to the `FormTypeExtensionInterface`
+ * calling to `FormRenderer::searchAndRenderBlock()` method for fields which were already rendered throw a `BadMethodCallException`
+ * removed the `regions` option of the `TimezoneType`
+ * removed the `$scale` argument of the `IntegerToLocalizedStringTransformer`
+ * removed `TemplatingExtension` and `TemplatingRendererEngine` classes, use Twig instead
+ * passing a null message when instantiating a `Symfony\Component\Form\FormError` is not allowed
+ * removed support for using `int` or `float` as data for the `NumberType` when the `input` option is set to `string`
+
4.4.0
-----
diff --git a/src/Symfony/Component/Form/ChoiceList/ChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ChoiceList.php
new file mode 100644
index 0000000000000..d386f88eba671
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/ChoiceList.php
@@ -0,0 +1,135 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList;
+
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceAttr;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFieldName;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLabel;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLoader;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceValue;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\GroupBy;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\PreferredChoice;
+use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
+use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A set of convenient static methods to create cacheable choice list options.
+ *
+ * @author Jules Pietri
+ */
+final class ChoiceList
+{
+ /**
+ * Creates a cacheable loader from any callable providing iterable choices.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable $choices A callable that must return iterable choices or grouped choices
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the loader
+ */
+ public static function lazy($formType, callable $choices, $vary = null): ChoiceLoader
+ {
+ return self::loader($formType, new CallbackChoiceLoader($choices), $vary);
+ }
+
+ /**
+ * Decorates a loader to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param ChoiceLoaderInterface $loader A loader responsible for creating loading choices or grouped choices
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the loader
+ */
+ public static function loader($formType, ChoiceLoaderInterface $loader, $vary = null): ChoiceLoader
+ {
+ return new ChoiceLoader($formType, $loader, $vary);
+ }
+
+ /**
+ * Decorates a "choice_value" callback to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable $value Any pseudo callable to create a unique string value from a choice
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
+ */
+ public static function value($formType, $value, $vary = null): ChoiceValue
+ {
+ return new ChoiceValue($formType, $value, $vary);
+ }
+
+ /**
+ * Decorates a "choice_label" option to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable|false $label Any pseudo callable to create a label from a choice or false to discard it
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
+ */
+ public static function label($formType, $label, $vary = null): ChoiceLabel
+ {
+ return new ChoiceLabel($formType, $label, $vary);
+ }
+
+ /**
+ * Decorates a "choice_name" callback to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable $fieldName Any pseudo callable to create a field name from a choice
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
+ */
+ public static function fieldName($formType, $fieldName, $vary = null): ChoiceFieldName
+ {
+ return new ChoiceFieldName($formType, $fieldName, $vary);
+ }
+
+ /**
+ * Decorates a "choice_attr" option to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable|array $attr Any pseudo callable or array to create html attributes from a choice
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
+ */
+ public static function attr($formType, $attr, $vary = null): ChoiceAttr
+ {
+ return new ChoiceAttr($formType, $attr, $vary);
+ }
+
+ /**
+ * Decorates a "group_by" callback to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable $groupBy Any pseudo callable to return a group name from a choice
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
+ */
+ public static function groupBy($formType, $groupBy, $vary = null): GroupBy
+ {
+ return new GroupBy($formType, $groupBy, $vary);
+ }
+
+ /**
+ * Decorates a "preferred_choices" option to make it cacheable.
+ *
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param callable|array $preferred Any pseudo callable or array to return a group name from a choice
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
+ */
+ public static function preferred($formType, $preferred, $vary = null): PreferredChoice
+ {
+ return new PreferredChoice($formType, $preferred, $vary);
+ }
+
+ /**
+ * Should not be instantiated.
+ */
+ private function __construct()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/AbstractStaticOption.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/AbstractStaticOption.php
new file mode 100644
index 0000000000000..2f8ac98078ffb
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/AbstractStaticOption.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
+use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A template decorator for static {@see ChoiceType} options.
+ *
+ * Used as fly weight for {@see CachingFactoryDecorator}.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+abstract class AbstractStaticOption
+{
+ private static $options = [];
+
+ /** @var bool|callable|string|array|\Closure|ChoiceLoaderInterface */
+ private $option;
+
+ /**
+ * @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
+ * @param mixed $option Any pseudo callable, array, string or bool to define a choice list option
+ * @param mixed|null $vary Dynamic data used to compute a unique hash when caching the option
+ */
+ final public function __construct($formType, $option, $vary = null)
+ {
+ if (!$formType instanceof FormTypeInterface && !$formType instanceof FormTypeExtensionInterface) {
+ throw new \TypeError(sprintf('Expected an instance of "%s" or "%s", but got "%s".', FormTypeInterface::class, FormTypeExtensionInterface::class, \is_object($formType) ? \get_class($formType) : \gettype($formType)));
+ }
+
+ $hash = CachingFactoryDecorator::generateHash([static::class, $formType, $vary]);
+
+ $this->option = self::$options[$hash] ?? self::$options[$hash] = $option;
+ }
+
+ /**
+ * @return mixed
+ */
+ final public function getOption()
+ {
+ return $this->option;
+ }
+
+ final public static function reset(): void
+ {
+ self::$options = [];
+ }
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceAttr.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceAttr.php
new file mode 100644
index 0000000000000..8de6956d16705
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceAttr.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "choice_attr" option.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class ChoiceAttr extends AbstractStaticOption
+{
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceFieldName.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceFieldName.php
new file mode 100644
index 0000000000000..0c71e20506d7a
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceFieldName.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "choice_name" callback.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class ChoiceFieldName extends AbstractStaticOption
+{
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLabel.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLabel.php
new file mode 100644
index 0000000000000..664a09081f36a
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLabel.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "choice_label" option.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class ChoiceLabel extends AbstractStaticOption
+{
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php
new file mode 100644
index 0000000000000..d8630dd854dbe
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "choice_loader" option.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class ChoiceLoader extends AbstractStaticOption implements ChoiceLoaderInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function loadChoiceList(callable $value = null)
+ {
+ return $this->getOption()->loadChoiceList($value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadChoicesForValues(array $values, callable $value = null)
+ {
+ return $this->getOption()->loadChoicesForValues($values, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadValuesForChoices(array $choices, callable $value = null)
+ {
+ $this->getOption()->loadValuesForChoices($choices, $value);
+ }
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceValue.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceValue.php
new file mode 100644
index 0000000000000..d96f1e9e83b80
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceValue.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "choice_value" callback.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class ChoiceValue extends AbstractStaticOption
+{
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/GroupBy.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/GroupBy.php
new file mode 100644
index 0000000000000..2ad492caf3923
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/GroupBy.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "group_by" callback.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class GroupBy extends AbstractStaticOption
+{
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/PreferredChoice.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/PreferredChoice.php
new file mode 100644
index 0000000000000..4aefd69ab3e8f
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/PreferredChoice.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Factory\Cache;
+
+use Symfony\Component\Form\FormTypeExtensionInterface;
+use Symfony\Component\Form\FormTypeInterface;
+
+/**
+ * A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
+ * which configures a "preferred_choices" option.
+ *
+ * @internal
+ *
+ * @author Jules Pietri
+ */
+final class PreferredChoice extends AbstractStaticOption
+{
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php
index 6fad79c094328..f7fe8c2465ff1 100644
--- a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php
@@ -20,6 +20,7 @@
* Caches the choice lists created by the decorated factory.
*
* @author Bernhard Schussek
+ * @author Jules Pietri
*/
class CachingFactoryDecorator implements ChoiceListFactoryInterface, ResetInterface
{
@@ -80,14 +81,19 @@ public function getDecoratedFactory()
/**
* {@inheritdoc}
*/
- public function createListFromChoices($choices, $value = null)
+ public function createListFromChoices(iterable $choices, $value = null)
{
if ($choices instanceof \Traversable) {
$choices = iterator_to_array($choices);
}
- // The value is not validated on purpose. The decorated factory may
- // decide which values to accept and which not.
+ // Only cache per value when needed. The value is not validated on purpose.
+ // The decorated factory may decide which values to accept and which not.
+ if ($value instanceof Cache\ChoiceValue) {
+ $value = $value->getOption();
+ } elseif ($value) {
+ return $this->decoratedFactory->createListFromChoices($choices, $value);
+ }
$hash = self::generateHash([$choices, $value], 'fromChoices');
@@ -103,6 +109,24 @@ public function createListFromChoices($choices, $value = null)
*/
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
{
+ $cache = true;
+
+ if ($loader instanceof Cache\ChoiceLoader) {
+ $loader = $loader->getOption();
+ } else {
+ $cache = false;
+ }
+
+ if ($value instanceof Cache\ChoiceValue) {
+ $value = $value->getOption();
+ } elseif ($value) {
+ $cache = false;
+ }
+
+ if (!$cache) {
+ return $this->decoratedFactory->createListFromLoader($loader, $value);
+ }
+
$hash = self::generateHash([$loader, $value], 'fromLoader');
if (!isset($this->lists[$hash])) {
@@ -117,8 +141,42 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
*/
public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null)
{
- // The input is not validated on purpose. This way, the decorated
- // factory may decide which input to accept and which not.
+ $cache = true;
+
+ if ($preferredChoices instanceof Cache\PreferredChoice) {
+ $preferredChoices = $preferredChoices->getOption();
+ } elseif ($preferredChoices) {
+ $cache = false;
+ }
+
+ if ($label instanceof Cache\ChoiceLabel) {
+ $label = $label->getOption();
+ } elseif (null !== $label) {
+ $cache = false;
+ }
+
+ if ($index instanceof Cache\ChoiceFieldName) {
+ $index = $index->getOption();
+ } elseif ($index) {
+ $cache = false;
+ }
+
+ if ($groupBy instanceof Cache\GroupBy) {
+ $groupBy = $groupBy->getOption();
+ } elseif ($groupBy) {
+ $cache = false;
+ }
+
+ if ($attr instanceof Cache\ChoiceAttr) {
+ $attr = $attr->getOption();
+ } elseif ($attr) {
+ $cache = false;
+ }
+
+ if (!$cache) {
+ return $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr);
+ }
+
$hash = self::generateHash([$list, $preferredChoices, $label, $index, $groupBy, $attr]);
if (!isset($this->views[$hash])) {
@@ -139,5 +197,6 @@ public function reset()
{
$this->lists = [];
$this->views = [];
+ Cache\AbstractStaticOption::reset();
}
}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php b/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php
index 04e414df5a079..7cb37e1a82c65 100644
--- a/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php
@@ -31,12 +31,9 @@ interface ChoiceListFactoryInterface
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
- * @param iterable $choices The choices
- * @param callable|null $value The callable generating the choice values
- *
* @return ChoiceListInterface The choice list
*/
- public function createListFromChoices($choices, $value = null);
+ public function createListFromChoices(iterable $choices, callable $value = null);
/**
* Creates a choice list that is loaded with the given loader.
@@ -45,11 +42,9 @@ public function createListFromChoices($choices, $value = null);
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
- * @param callable|null $value The callable generating the choice values
- *
* @return ChoiceListInterface The choice list
*/
- public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null);
+ public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null);
/**
* Creates a view for the given choice list.
@@ -80,11 +75,9 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
* @param array|callable|null $preferredChoices The preferred choices
* @param callable|false|null $label The callable generating the choice labels;
* pass false to discard the label
- * @param callable|null $index The callable generating the view indices
- * @param callable|null $groupBy The callable generating the group names
* @param array|callable|null $attr The callable generating the HTML attributes
*
* @return ChoiceListView The choice list view
*/
- public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null);
+ public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, callable $index = null, callable $groupBy = null, $attr = null);
}
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php
index e401351660c1f..1b184b6ab4ccf 100644
--- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php
@@ -29,7 +29,7 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
/**
* {@inheritdoc}
*/
- public function createListFromChoices($choices, $value = null)
+ public function createListFromChoices(iterable $choices, callable $value = null)
{
return new ArrayChoiceList($choices, $value);
}
@@ -37,7 +37,7 @@ public function createListFromChoices($choices, $value = null)
/**
* {@inheritdoc}
*/
- public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
+ public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null)
{
return new LazyChoiceList($loader, $value);
}
@@ -45,7 +45,7 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
/**
* {@inheritdoc}
*/
- public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null)
+ public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, callable $index = null, callable $groupBy = null, $attr = null)
{
$preferredViews = [];
$preferredViewsOrder = [];
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php
index 4ce1d849a010a..42b8a022c41f4 100644
--- a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php
@@ -59,13 +59,12 @@ public function getDecoratedFactory()
/**
* {@inheritdoc}
*
- * @param iterable $choices The choices
- * @param callable|string|PropertyPath|null $value The callable or path for
- * generating the choice values
+ * @param callable|string|PropertyPath|null $value The callable or path for
+ * generating the choice values
*
* @return ChoiceListInterface The choice list
*/
- public function createListFromChoices($choices, $value = null)
+ public function createListFromChoices(iterable $choices, $value = null)
{
if (\is_string($value)) {
$value = new PropertyPath($value);
diff --git a/src/Symfony/Component/Form/ChoiceList/Loader/AbstractChoiceLoader.php b/src/Symfony/Component/Form/ChoiceList/Loader/AbstractChoiceLoader.php
new file mode 100644
index 0000000000000..ea736a52c683f
--- /dev/null
+++ b/src/Symfony/Component/Form/ChoiceList/Loader/AbstractChoiceLoader.php
@@ -0,0 +1,86 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\ChoiceList\Loader;
+
+use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
+
+/**
+ * @author Jules Pietri
+ */
+abstract class AbstractChoiceLoader implements ChoiceLoaderInterface
+{
+ /**
+ * The loaded choice list.
+ *
+ * @var ArrayChoiceList
+ */
+ private $choiceList;
+
+ /**
+ * @final
+ *
+ * {@inheritdoc}
+ */
+ public function loadChoiceList(callable $value = null)
+ {
+ return $this->choiceList ?? ($this->choiceList = new ArrayChoiceList($this->loadChoices(), $value));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadChoicesForValues(array $values, callable $value = null)
+ {
+ if (!$values) {
+ return [];
+ }
+
+ if ($this->choiceList) {
+ return $this->choiceList->getChoicesForValues($values);
+ }
+
+ return $this->doLoadChoicesForValues($values, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadValuesForChoices(array $choices, callable $value = null)
+ {
+ if (!$choices) {
+ return [];
+ }
+
+ if ($value) {
+ // if a value callback exists, use it
+ return array_map($value, $choices);
+ }
+
+ if ($this->choiceList) {
+ return $this->choiceList->getValuesForChoices($choices);
+ }
+
+ return $this->doLoadValuesForChoices($choices);
+ }
+
+ abstract protected function loadChoices(): iterable;
+
+ protected function doLoadChoicesForValues(array $values, ?callable $value): array
+ {
+ return $this->loadChoiceList($value)->getChoicesForValues($values);
+ }
+
+ protected function doLoadValuesForChoices(array $choices): array
+ {
+ return $this->loadChoiceList()->getValuesForChoices($choices);
+ }
+}
diff --git a/src/Symfony/Component/Form/ChoiceList/Loader/CallbackChoiceLoader.php b/src/Symfony/Component/Form/ChoiceList/Loader/CallbackChoiceLoader.php
index 2d6782f558aea..1811d434e8f26 100644
--- a/src/Symfony/Component/Form/ChoiceList/Loader/CallbackChoiceLoader.php
+++ b/src/Symfony/Component/Form/ChoiceList/Loader/CallbackChoiceLoader.php
@@ -11,67 +11,25 @@
namespace Symfony\Component\Form\ChoiceList\Loader;
-use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
-
/**
- * Loads an {@link ArrayChoiceList} instance from a callable returning an array of choices.
+ * Loads an {@link ArrayChoiceList} instance from a callable returning iterable choices.
*
* @author Jules Pietri
*/
-class CallbackChoiceLoader implements ChoiceLoaderInterface
+class CallbackChoiceLoader extends AbstractChoiceLoader
{
private $callback;
/**
- * The loaded choice list.
- *
- * @var ArrayChoiceList
- */
- private $choiceList;
-
- /**
- * @param callable $callback The callable returning an array of choices
+ * @param callable $callback The callable returning iterable choices
*/
public function __construct(callable $callback)
{
$this->callback = $callback;
}
- /**
- * {@inheritdoc}
- */
- public function loadChoiceList($value = null)
+ protected function loadChoices(): iterable
{
- if (null !== $this->choiceList) {
- return $this->choiceList;
- }
-
- return $this->choiceList = new ArrayChoiceList(($this->callback)(), $value);
- }
-
- /**
- * {@inheritdoc}
- */
- public function loadChoicesForValues(array $values, $value = null)
- {
- // Optimize
- if (empty($values)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getChoicesForValues($values);
- }
-
- /**
- * {@inheritdoc}
- */
- public function loadValuesForChoices(array $choices, $value = null)
- {
- // Optimize
- if (empty($choices)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getValuesForChoices($choices);
+ return ($this->callback)();
}
}
diff --git a/src/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.php b/src/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.php
index 31d47d539cfb5..507735d1b3aa2 100644
--- a/src/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.php
+++ b/src/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.php
@@ -36,7 +36,7 @@ interface ChoiceLoaderInterface
*
* @return ChoiceListInterface The loaded choice list
*/
- public function loadChoiceList($value = null);
+ public function loadChoiceList(callable $value = null);
/**
* Loads the choices corresponding to the given values.
@@ -54,7 +54,7 @@ public function loadChoiceList($value = null);
*
* @return array An array of choices
*/
- public function loadChoicesForValues(array $values, $value = null);
+ public function loadChoicesForValues(array $values, callable $value = null);
/**
* Loads the values corresponding to the given choices.
@@ -72,5 +72,5 @@ public function loadChoicesForValues(array $values, $value = null);
*
* @return string[] An array of choice values
*/
- public function loadValuesForChoices(array $choices, $value = null);
+ public function loadValuesForChoices(array $choices, callable $value = null);
}
diff --git a/src/Symfony/Component/Form/ChoiceList/Loader/IntlCallbackChoiceLoader.php b/src/Symfony/Component/Form/ChoiceList/Loader/IntlCallbackChoiceLoader.php
index 1a119bb96cdcf..546937b900c0c 100644
--- a/src/Symfony/Component/Form/ChoiceList/Loader/IntlCallbackChoiceLoader.php
+++ b/src/Symfony/Component/Form/ChoiceList/Loader/IntlCallbackChoiceLoader.php
@@ -22,33 +22,23 @@ class IntlCallbackChoiceLoader extends CallbackChoiceLoader
/**
* {@inheritdoc}
*/
- public function loadChoicesForValues(array $values, $value = null)
+ public function loadChoicesForValues(array $values, callable $value = null)
{
- // Optimize
- $values = array_filter($values);
- if (empty($values)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getChoicesForValues($values);
+ return parent::loadChoicesForValues(array_filter($values), $value);
}
/**
* {@inheritdoc}
*/
- public function loadValuesForChoices(array $choices, $value = null)
+ public function loadValuesForChoices(array $choices, callable $value = null)
{
- // Optimize
$choices = array_filter($choices);
- if (empty($choices)) {
- return [];
- }
// If no callable is set, choices are the same as values
if (null === $value) {
return $choices;
}
- return $this->loadChoiceList($value)->getValuesForChoices($choices);
+ return parent::loadValuesForChoices($choices, $value);
}
}
diff --git a/src/Symfony/Component/Form/ChoiceList/View/ChoiceGroupView.php b/src/Symfony/Component/Form/ChoiceList/View/ChoiceGroupView.php
index dd80486c9acea..0c0654ed9f4e2 100644
--- a/src/Symfony/Component/Form/ChoiceList/View/ChoiceGroupView.php
+++ b/src/Symfony/Component/Form/ChoiceList/View/ChoiceGroupView.php
@@ -24,10 +24,9 @@ class ChoiceGroupView implements \IteratorAggregate
/**
* Creates a new choice group view.
*
- * @param string $label The label of the group
* @param ChoiceGroupView[]|ChoiceView[] $choices the choice views in the group
*/
- public function __construct($label, array $choices = [])
+ public function __construct(string $label, array $choices = [])
{
$this->label = $label;
$this->choices = $choices;
diff --git a/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php b/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
index 17828344c46f1..e085795bb51c0 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
@@ -106,9 +106,17 @@ protected function collectOptions(ResolvedFormTypeInterface $type)
$this->extensions = array_keys($this->extensions);
}
- protected function getOptionDefinition(OptionsResolver $optionsResolver, $option)
+ protected function getOptionDefinition(OptionsResolver $optionsResolver, string $option)
{
- $definition = [
+ $definition = [];
+
+ if ($info = $optionsResolver->getInfo($option)) {
+ $definition = [
+ 'info' => $info,
+ ];
+ }
+
+ $definition += [
'required' => $optionsResolver->isRequired($option),
'deprecated' => $optionsResolver->isDeprecated($option),
];
diff --git a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
index 4ef4b4a3257b3..20f827bed319a 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
@@ -73,6 +73,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
}
}
$map += [
+ 'info' => 'info',
'required' => 'required',
'default' => 'default',
'allowed_types' => 'allowedTypes',
diff --git a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
index 6dc9ac48ac1c5..17df85f0fa5b2 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
@@ -114,6 +114,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
];
}
$map += [
+ 'Info' => 'info',
'Required' => 'required',
'Default' => 'default',
'Allowed types' => 'allowedTypes',
diff --git a/src/Symfony/Component/Form/DataMapperInterface.php b/src/Symfony/Component/Form/DataMapperInterface.php
index dee8f784910ea..1e0583aa08fff 100644
--- a/src/Symfony/Component/Form/DataMapperInterface.php
+++ b/src/Symfony/Component/Form/DataMapperInterface.php
@@ -22,12 +22,12 @@ interface DataMapperInterface
* The method is responsible for calling {@link FormInterface::setData()}
* on the children of compound forms, defining their underlying model data.
*
- * @param mixed $viewData View data of the compound form being initialized
- * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances
+ * @param mixed $viewData View data of the compound form being initialized
+ * @param FormInterface[]|iterable $forms A list of {@link FormInterface} instances
*
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported
*/
- public function mapDataToForms($viewData, $forms);
+ public function mapDataToForms($viewData, iterable $forms);
/**
* Maps the model data of a list of children forms into the view data of their parent.
@@ -52,11 +52,11 @@ public function mapDataToForms($viewData, $forms);
* The model data can be an array or an object, so this second argument is always passed
* by reference.
*
- * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances
- * @param mixed $viewData The compound form's view data that get mapped
- * its children model data
+ * @param FormInterface[]|iterable $forms A list of {@link FormInterface} instances
+ * @param mixed $viewData The compound form's view data that get mapped
+ * its children model data
*
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported
*/
- public function mapFormsToData($forms, &$viewData);
+ public function mapFormsToData(iterable $forms, &$viewData);
}
diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php
index 2be2a59d60297..38fe8eec157be 100644
--- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php
+++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php
@@ -19,7 +19,6 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\Form\FormTypeExtensionInterface;
/**
* Adds all services with the tags "form.type", "form.type_extension" and
@@ -96,13 +95,9 @@ private function processFormTypeExtensions(ContainerBuilder $container): array
$typeExtensionClass = $container->getParameterBag()->resolveValue($serviceDefinition->getClass());
if (isset($tag[0]['extended_type'])) {
- if (!method_exists($typeExtensionClass, 'getExtendedTypes')) {
- @trigger_error(sprintf('Not implementing the "%s::getExtendedTypes()" method in "%s" is deprecated since Symfony 4.2.', FormTypeExtensionInterface::class, $typeExtensionClass), E_USER_DEPRECATED);
- }
-
$typeExtensions[$tag[0]['extended_type']][] = new Reference($serviceId);
$typeExtensionsClasses[] = $typeExtensionClass;
- } elseif (method_exists($typeExtensionClass, 'getExtendedTypes')) {
+ } else {
$extendsTypes = false;
$typeExtensionsClasses[] = $typeExtensionClass;
@@ -114,8 +109,6 @@ private function processFormTypeExtensions(ContainerBuilder $container): array
if (!$extendsTypes) {
throw new InvalidArgumentException(sprintf('The getExtendedTypes() method for service "%s" does not return any extended types.', $serviceId));
}
- } else {
- throw new InvalidArgumentException(sprintf('"%s" tagged services have to implement the static getExtendedTypes() method. Class "%s" for service "%s" does not implement it.', $this->formTypeExtensionTag, $typeExtensionClass, $serviceId));
}
}
diff --git a/src/Symfony/Component/Form/Event/PostSetDataEvent.php b/src/Symfony/Component/Form/Event/PostSetDataEvent.php
index 442f60d13d6a8..eef537452acd9 100644
--- a/src/Symfony/Component/Form/Event/PostSetDataEvent.php
+++ b/src/Symfony/Component/Form/Event/PostSetDataEvent.php
@@ -17,9 +17,7 @@
* This event is dispatched at the end of the Form::setData() method.
*
* This event is mostly here for reading data after having pre-populated the form.
- *
- * @final since Symfony 4.4
*/
-class PostSetDataEvent extends FormEvent
+final class PostSetDataEvent extends FormEvent
{
}
diff --git a/src/Symfony/Component/Form/Event/PostSubmitEvent.php b/src/Symfony/Component/Form/Event/PostSubmitEvent.php
index 9ddb2f8b0e42a..aa3775f5c1748 100644
--- a/src/Symfony/Component/Form/Event/PostSubmitEvent.php
+++ b/src/Symfony/Component/Form/Event/PostSubmitEvent.php
@@ -18,9 +18,7 @@
* once the model and view data have been denormalized.
*
* It can be used to fetch data after denormalization.
- *
- * @final since Symfony 4.4
*/
-class PostSubmitEvent extends FormEvent
+final class PostSubmitEvent extends FormEvent
{
}
diff --git a/src/Symfony/Component/Form/Event/PreSetDataEvent.php b/src/Symfony/Component/Form/Event/PreSetDataEvent.php
index 678a62918e411..5450400e06820 100644
--- a/src/Symfony/Component/Form/Event/PreSetDataEvent.php
+++ b/src/Symfony/Component/Form/Event/PreSetDataEvent.php
@@ -19,9 +19,7 @@
* It can be used to:
* - Modify the data given during pre-population;
* - Modify a form depending on the pre-populated data (adding or removing fields dynamically).
- *
- * @final since Symfony 4.4
*/
-class PreSetDataEvent extends FormEvent
+final class PreSetDataEvent extends FormEvent
{
}
diff --git a/src/Symfony/Component/Form/Event/PreSubmitEvent.php b/src/Symfony/Component/Form/Event/PreSubmitEvent.php
index 5f34a2feebf19..a72ac5d16028a 100644
--- a/src/Symfony/Component/Form/Event/PreSubmitEvent.php
+++ b/src/Symfony/Component/Form/Event/PreSubmitEvent.php
@@ -19,9 +19,7 @@
* It can be used to:
* - Change data from the request, before submitting the data to the form.
* - Add or remove form fields, before submitting the data to the form.
- *
- * @final since Symfony 4.4
*/
-class PreSubmitEvent extends FormEvent
+final class PreSubmitEvent extends FormEvent
{
}
diff --git a/src/Symfony/Component/Form/Event/SubmitEvent.php b/src/Symfony/Component/Form/Event/SubmitEvent.php
index b3f16fc8fd446..71d3b06d47457 100644
--- a/src/Symfony/Component/Form/Event/SubmitEvent.php
+++ b/src/Symfony/Component/Form/Event/SubmitEvent.php
@@ -18,9 +18,7 @@
* transforms back the normalized data to the model and view data.
*
* It can be used to change data from the normalized representation of the data.
- *
- * @final since Symfony 4.4
*/
-class SubmitEvent extends FormEvent
+final class SubmitEvent extends FormEvent
{
}
diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php
index 1f3431cb49ea7..e3eae88b33543 100644
--- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php
+++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php
@@ -19,7 +19,6 @@
use Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -33,14 +32,8 @@ class CoreExtension extends AbstractExtension
private $choiceListFactory;
private $translator;
- /**
- * @param TranslatorInterface|null $translator
- */
- public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, $translator = null)
+ public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, TranslatorInterface $translator = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 3 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
$this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor));
$this->translator = $translator;
diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php
index ddc771fe25ae8..f03d1ad86a006 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/CheckboxListMapper.php
@@ -28,7 +28,7 @@ class CheckboxListMapper implements DataMapperInterface
/**
* {@inheritdoc}
*/
- public function mapDataToForms($choices, $checkboxes)
+ public function mapDataToForms($choices, iterable $checkboxes)
{
if (null === $choices) {
$choices = [];
@@ -47,7 +47,7 @@ public function mapDataToForms($choices, $checkboxes)
/**
* {@inheritdoc}
*/
- public function mapFormsToData($checkboxes, &$choices)
+ public function mapFormsToData(iterable $checkboxes, &$choices)
{
if (!\is_array($choices)) {
throw new UnexpectedTypeException($choices, 'array');
diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
index 657d9d63bec26..7a3d8db6a4ecc 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
@@ -33,7 +33,7 @@ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
/**
* {@inheritdoc}
*/
- public function mapDataToForms($data, $forms)
+ public function mapDataToForms($data, iterable $forms)
{
$empty = null === $data || [] === $data;
@@ -56,7 +56,7 @@ public function mapDataToForms($data, $forms)
/**
* {@inheritdoc}
*/
- public function mapFormsToData($forms, &$data)
+ public function mapFormsToData(iterable $forms, &$data)
{
if (null === $data) {
return;
diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php
index d2ec810f70c4d..bb34c912a2ebe 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/RadioListMapper.php
@@ -28,7 +28,7 @@ class RadioListMapper implements DataMapperInterface
/**
* {@inheritdoc}
*/
- public function mapDataToForms($choice, $radios)
+ public function mapDataToForms($choice, iterable $radios)
{
if (!\is_string($choice)) {
throw new UnexpectedTypeException($choice, 'string');
@@ -43,7 +43,7 @@ public function mapDataToForms($choice, $radios)
/**
* {@inheritdoc}
*/
- public function mapFormsToData($radios, &$choice)
+ public function mapFormsToData(iterable $radios, &$choice)
{
if (null !== $choice && !\is_string($choice)) {
throw new UnexpectedTypeException($choice, 'null or string');
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php
index 376a00c11f5f8..6eb40af9d8e68 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php
@@ -26,8 +26,6 @@ final class DateTimeImmutableToDateTimeTransformer implements DataTransformerInt
*
* @param \DateTimeImmutable|null $value A DateTimeImmutable object
*
- * @return \DateTime|null A \DateTime object
- *
* @throws TransformationFailedException If the given value is not a \DateTimeImmutable
*/
public function transform($value): ?\DateTime
@@ -52,8 +50,6 @@ public function transform($value): ?\DateTime
*
* @param \DateTime|null $value A DateTime object
*
- * @return \DateTimeImmutable|null A DateTimeImmutable object
- *
* @throws TransformationFailedException If the given value is not a \DateTime
*/
public function reverseTransform($value): ?\DateTimeImmutable
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
index 325f61f11232e..2dfbbf17d60d1 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
@@ -158,7 +158,7 @@ public function reverseTransform($value)
*
* @throws TransformationFailedException in case the date formatter can not be constructed
*/
- protected function getIntlDateFormatter($ignoreTimezone = false)
+ protected function getIntlDateFormatter(bool $ignoreTimezone = false)
{
$dateFormat = $this->dateFormat;
$timeFormat = $this->timeFormat;
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
index 392aa49e51a19..68ba2c0227da4 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php
@@ -27,15 +27,8 @@ class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransfo
* @param bool $grouping Whether thousands should be grouped
* @param int $roundingMode One of the ROUND_ constants in this class
*/
- public function __construct($grouping = false, $roundingMode = self::ROUND_DOWN)
+ public function __construct(?bool $grouping = false, ?int $roundingMode = self::ROUND_DOWN)
{
- if (\is_int($grouping) || \is_bool($roundingMode) || 2 < \func_num_args()) {
- @trigger_error(sprintf('Passing a precision as the first value to %s::__construct() is deprecated since Symfony 4.2 and support for it will be dropped in 5.0.', __CLASS__), E_USER_DEPRECATED);
-
- $grouping = $roundingMode;
- $roundingMode = 2 < \func_num_args() ? func_get_arg(2) : self::ROUND_DOWN;
- }
-
parent::__construct(0, $grouping, $roundingMode);
}
diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php
index f5e73849b3700..95763c26c7422 100644
--- a/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php
+++ b/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php
@@ -15,7 +15,6 @@
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -25,14 +24,8 @@ class TransformationFailureListener implements EventSubscriberInterface
{
private $translator;
- /**
- * @param TranslatorInterface|null $translator
- */
- public function __construct($translator = null)
+ public function __construct(TranslatorInterface $translator = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->translator = $translator;
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php
index 2b29c5ad9bf16..2741a9afd4171 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php
@@ -60,6 +60,9 @@ public function configureOptions(OptionsResolver $resolver)
'empty_data' => $emptyData,
'compound' => false,
'false_values' => [null],
+ 'is_empty_callback' => static function ($modelData): bool {
+ return false === $modelData;
+ },
]);
$resolver->setAllowedTypes('false_values', 'array');
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
index 4be88149770f8..90e973fb7a0bd 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
@@ -13,6 +13,13 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceAttr;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFieldName;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLabel;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLoader;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceValue;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\GroupBy;
+use Symfony\Component\Form\ChoiceList\Factory\Cache\PreferredChoice;
use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
@@ -324,13 +331,13 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setAllowedTypes('choices', ['null', 'array', '\Traversable']);
$resolver->setAllowedTypes('choice_translation_domain', ['null', 'bool', 'string']);
- $resolver->setAllowedTypes('choice_loader', ['null', 'Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface']);
- $resolver->setAllowedTypes('choice_label', ['null', 'bool', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath']);
- $resolver->setAllowedTypes('choice_name', ['null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath']);
- $resolver->setAllowedTypes('choice_value', ['null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath']);
- $resolver->setAllowedTypes('choice_attr', ['null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath']);
- $resolver->setAllowedTypes('preferred_choices', ['array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath']);
- $resolver->setAllowedTypes('group_by', ['null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath']);
+ $resolver->setAllowedTypes('choice_loader', ['null', 'Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface', ChoiceLoader::class]);
+ $resolver->setAllowedTypes('choice_label', ['null', 'bool', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath', ChoiceLabel::class]);
+ $resolver->setAllowedTypes('choice_name', ['null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath', ChoiceFieldName::class]);
+ $resolver->setAllowedTypes('choice_value', ['null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath', ChoiceValue::class]);
+ $resolver->setAllowedTypes('choice_attr', ['null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath', ChoiceAttr::class]);
+ $resolver->setAllowedTypes('preferred_choices', ['array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath', PreferredChoice::class]);
+ $resolver->setAllowedTypes('group_by', ['null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath', GroupBy::class]);
}
/**
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php
index be941d2d55e75..d2d3aee80aab7 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php
@@ -12,28 +12,14 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
-use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
use Symfony\Component\Intl\Countries;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class CountryType extends AbstractType implements ChoiceLoaderInterface
+class CountryType extends AbstractType
{
- /**
- * Country loaded choice list.
- *
- * The choices are lazy loaded and generated from the Intl component.
- *
- * {@link \Symfony\Component\Intl\Intl::getRegionBundle()}.
- *
- * @var ArrayChoiceList
- *
- * @deprecated since Symfony 4.1
- */
- private $choiceList;
-
/**
* {@inheritdoc}
*/
@@ -44,9 +30,9 @@ public function configureOptions(OptionsResolver $resolver)
$choiceTranslationLocale = $options['choice_translation_locale'];
$alpha3 = $options['alpha3'];
- return new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale, $alpha3) {
+ return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale, $alpha3) {
return array_flip($alpha3 ? Countries::getAlpha3Names($choiceTranslationLocale) : Countries::getNames($choiceTranslationLocale));
- });
+ }), [$choiceTranslationLocale, $alpha3]);
},
'choice_translation_domain' => false,
'choice_translation_locale' => null,
@@ -72,61 +58,4 @@ public function getBlockPrefix()
{
return 'country';
}
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoiceList($value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- if (null !== $this->choiceList) {
- return $this->choiceList;
- }
-
- return $this->choiceList = new ArrayChoiceList(array_flip(Countries::getNames()), $value);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoicesForValues(array $values, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $values = array_filter($values);
- if (empty($values)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getChoicesForValues($values);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadValuesForChoices(array $choices, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $choices = array_filter($choices);
- if (empty($choices)) {
- return [];
- }
-
- // If no callable is set, choices are the same as values
- if (null === $value) {
- return $choices;
- }
-
- return $this->loadChoiceList($value)->getValuesForChoices($choices);
- }
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php
index d1d71c32a7ce3..4506bf488f981 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php
@@ -12,28 +12,14 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
-use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
use Symfony\Component\Intl\Currencies;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class CurrencyType extends AbstractType implements ChoiceLoaderInterface
+class CurrencyType extends AbstractType
{
- /**
- * Currency loaded choice list.
- *
- * The choices are lazy loaded and generated from the Intl component.
- *
- * {@link \Symfony\Component\Intl\Intl::getCurrencyBundle()}.
- *
- * @var ArrayChoiceList
- *
- * @deprecated since Symfony 4.1
- */
- private $choiceList;
-
/**
* {@inheritdoc}
*/
@@ -43,9 +29,9 @@ public function configureOptions(OptionsResolver $resolver)
'choice_loader' => function (Options $options) {
$choiceTranslationLocale = $options['choice_translation_locale'];
- return new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale) {
+ return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale) {
return array_flip(Currencies::getNames($choiceTranslationLocale));
- });
+ }), $choiceTranslationLocale);
},
'choice_translation_domain' => false,
'choice_translation_locale' => null,
@@ -69,61 +55,4 @@ public function getBlockPrefix()
{
return 'currency';
}
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoiceList($value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- if (null !== $this->choiceList) {
- return $this->choiceList;
- }
-
- return $this->choiceList = new ArrayChoiceList(array_flip(Currencies::getNames()), $value);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoicesForValues(array $values, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $values = array_filter($values);
- if (empty($values)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getChoicesForValues($values);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadValuesForChoices(array $choices, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $choices = array_filter($choices);
- if (empty($choices)) {
- return [];
- }
-
- // If no callable is set, choices are the same as values
- if (null === $value) {
- return $choices;
- }
-
- return $this->loadChoiceList($value)->getValuesForChoices($choices);
- }
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
index 0959ee7d321f3..d26d0fd5752bb 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
@@ -171,10 +172,6 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$dateOptions['input'] = $timeOptions['input'] = 'array';
$dateOptions['error_bubbling'] = $timeOptions['error_bubbling'] = true;
- if (isset($dateOptions['format']) && DateType::HTML5_FORMAT !== $dateOptions['format']) {
- $dateOptions['html5'] = false;
- }
-
$builder
->addViewTransformer(new DataTransformerChain([
new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts),
@@ -312,37 +309,33 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setAllowedTypes('input_format', 'string');
- $resolver->setDeprecated('date_format', function (Options $options, $dateFormat) {
+ $resolver->setNormalizer('date_format', function (Options $options, $dateFormat) {
if (null !== $dateFormat && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
- return sprintf('Using the "date_format" option of %s with an HTML5 date widget is deprecated since Symfony 4.3 and will lead to an exception in 5.0.', self::class);
- //throw new LogicException(sprintf('Cannot use the "date_format" option of the %s with an HTML5 date.', self::class));
+ throw new LogicException(sprintf('Cannot use the "date_format" option of the %s with an HTML5 date.', self::class));
}
- return '';
+ return $dateFormat;
});
- $resolver->setDeprecated('date_widget', function (Options $options, $dateWidget) {
+ $resolver->setNormalizer('date_widget', function (Options $options, $dateWidget) {
if (null !== $dateWidget && 'single_text' === $options['widget']) {
- return sprintf('Using the "date_widget" option of %s when the "widget" option is set to "single_text" is deprecated since Symfony 4.3 and will lead to an exception in 5.0.', self::class);
- //throw new LogicException(sprintf('Cannot use the "date_widget" option of the %s when the "widget" option is set to "single_text".', self::class));
+ throw new LogicException(sprintf('Cannot use the "date_widget" option of the %s when the "widget" option is set to "single_text".', self::class));
}
- return '';
+ return $dateWidget;
});
- $resolver->setDeprecated('time_widget', function (Options $options, $timeWidget) {
+ $resolver->setNormalizer('time_widget', function (Options $options, $timeWidget) {
if (null !== $timeWidget && 'single_text' === $options['widget']) {
- return sprintf('Using the "time_widget" option of %s when the "widget" option is set to "single_text" is deprecated since Symfony 4.3 and will lead to an exception in 5.0.', self::class);
- //throw new LogicException(sprintf('Cannot use the "time_widget" option of the %s when the "widget" option is set to "single_text".', self::class));
+ throw new LogicException(sprintf('Cannot use the "time_widget" option of the %s when the "widget" option is set to "single_text".', self::class));
}
- return '';
+ return $timeWidget;
});
- $resolver->setDeprecated('html5', function (Options $options, $html5) {
+ $resolver->setNormalizer('html5', function (Options $options, $html5) {
if ($html5 && self::HTML5_FORMAT !== $options['format']) {
- return sprintf('Using a custom format when the "html5" option of %s is enabled is deprecated since Symfony 4.3 and will lead to an exception in 5.0.', self::class);
- //throw new LogicException(sprintf('Cannot use the "format" option of %s when the "html5" option is disabled.', self::class));
+ throw new LogicException(sprintf('Cannot use the "format" option of %s when the "html5" option is enabled.', self::class));
}
- return '';
+ return $html5;
});
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php
index 58c4bbcf339d9..a187798cdbd97 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
@@ -322,13 +323,12 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setAllowedTypes('days', 'array');
$resolver->setAllowedTypes('input_format', 'string');
- $resolver->setDeprecated('html5', function (Options $options, $html5) {
+ $resolver->setNormalizer('html5', function (Options $options, $html5) {
if ($html5 && 'single_text' === $options['widget'] && self::HTML5_FORMAT !== $options['format']) {
- return sprintf('Using a custom format when the "html5" option of %s is enabled is deprecated since Symfony 4.3 and will lead to an exception in 5.0.', self::class);
- //throw new LogicException(sprintf('Cannot use the "format" option of %s when the "html5" option is disabled.', self::class));
+ throw new LogicException(sprintf('Cannot use the "format" option of %s when the "html5" option is enabled.', self::class));
}
- return '';
+ return $html5;
});
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/EmailType.php b/src/Symfony/Component/Form/Extension/Core/Type/EmailType.php
index 1bc1019ab9f26..b5e7a6de36aaa 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/EmailType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/EmailType.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormView;
class EmailType extends AbstractType
{
@@ -23,6 +25,14 @@ public function getParent()
return TextType::class;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function buildView(FormView $view, FormInterface $form, array $options)
+ {
+ $view->vars['attr']['inputmode'] = $options['attr']['inputmode'] ?? 'email';
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php
index 52368293f45d1..ce535394d9938 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php
@@ -20,7 +20,6 @@
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class FileType extends AbstractType
@@ -36,14 +35,8 @@ class FileType extends AbstractType
private $translator;
- /**
- * @param TranslatorInterface|null $translator
- */
- public function __construct($translator = null)
+ public function __construct(TranslatorInterface $translator = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->translator = $translator;
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
index 14302617454da..da592985f9041 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
@@ -15,6 +15,7 @@
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormConfigBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
@@ -58,6 +59,14 @@ public function buildForm(FormBuilderInterface $builder, array $options)
if ($options['trim']) {
$builder->addEventSubscriber(new TrimListener());
}
+
+ if (!method_exists($builder, 'setIsEmptyCallback')) {
+ trigger_deprecation('symfony/form', '5.1', 'Not implementing the "%s::setIsEmptyCallback()" method in "%s" is deprecated.', FormConfigBuilderInterface::class, \get_class($builder));
+
+ return;
+ }
+
+ $builder->setIsEmptyCallback($options['is_empty_callback']);
}
/**
@@ -190,6 +199,7 @@ public function configureOptions(OptionsResolver $resolver)
'help_attr' => [],
'help_html' => false,
'help_translation_parameters' => [],
+ 'is_empty_callback' => null,
]);
$resolver->setAllowedTypes('label_attr', 'array');
@@ -197,6 +207,7 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setAllowedTypes('help', ['string', 'null']);
$resolver->setAllowedTypes('help_attr', 'array');
$resolver->setAllowedTypes('help_html', 'bool');
+ $resolver->setAllowedTypes('is_empty_callback', ['null', 'callable']);
}
/**
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
index 540cdb6f79db3..7555bf41b2566 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
@@ -59,10 +59,6 @@ public function configureOptions(OptionsResolver $resolver)
IntegerToLocalizedStringTransformer::ROUND_UP,
IntegerToLocalizedStringTransformer::ROUND_CEILING,
]);
-
- $resolver->setDefined('scale');
- $resolver->setAllowedTypes('scale', ['null', 'int']);
- $resolver->setDeprecated('scale');
}
/**
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php
index b4b382b85ddf8..c5d1ac097740c 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php
@@ -12,28 +12,16 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
-use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
+use Symfony\Component\Form\Exception\LogicException;
+use Symfony\Component\Intl\Exception\MissingResourceException;
use Symfony\Component\Intl\Languages;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class LanguageType extends AbstractType implements ChoiceLoaderInterface
+class LanguageType extends AbstractType
{
- /**
- * Language loaded choice list.
- *
- * The choices are lazy loaded and generated from the Intl component.
- *
- * {@link \Symfony\Component\Intl\Intl::getLanguageBundle()}.
- *
- * @var ArrayChoiceList
- *
- * @deprecated since Symfony 4.1
- */
- private $choiceList;
-
/**
* {@inheritdoc}
*/
@@ -42,19 +30,43 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setDefaults([
'choice_loader' => function (Options $options) {
$choiceTranslationLocale = $options['choice_translation_locale'];
- $alpha3 = $options['alpha3'];
-
- return new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale, $alpha3) {
- return array_flip($alpha3 ? Languages::getAlpha3Names($choiceTranslationLocale) : Languages::getNames($choiceTranslationLocale));
- });
+ $useAlpha3Codes = $options['alpha3'];
+ $choiceSelfTranslation = $options['choice_self_translation'];
+
+ return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale, $useAlpha3Codes, $choiceSelfTranslation) {
+ if (true === $choiceSelfTranslation) {
+ foreach (Languages::getLanguageCodes() as $alpha2Code) {
+ try {
+ $languageCode = $useAlpha3Codes ? Languages::getAlpha3Code($alpha2Code) : $alpha2Code;
+ $languagesList[$languageCode] = Languages::getName($alpha2Code, $alpha2Code);
+ } catch (MissingResourceException $e) {
+ // ignore errors like "Couldn't read the indices for the locale 'meta'"
+ }
+ }
+ } else {
+ $languagesList = $useAlpha3Codes ? Languages::getAlpha3Names($choiceTranslationLocale) : Languages::getNames($choiceTranslationLocale);
+ }
+
+ return array_flip($languagesList);
+ }), [$choiceTranslationLocale, $useAlpha3Codes, $choiceSelfTranslation]);
},
'choice_translation_domain' => false,
'choice_translation_locale' => null,
'alpha3' => false,
+ 'choice_self_translation' => false,
]);
+ $resolver->setAllowedTypes('choice_self_translation', ['bool']);
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
$resolver->setAllowedTypes('alpha3', 'bool');
+
+ $resolver->setNormalizer('choice_self_translation', function (Options $options, $value) {
+ if (true === $value && $options['choice_translation_locale']) {
+ throw new LogicException('Cannot use the "choice_self_translation" and "choice_translation_locale" options at the same time. Remove one of them.');
+ }
+
+ return $value;
+ });
}
/**
@@ -72,61 +84,4 @@ public function getBlockPrefix()
{
return 'language';
}
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoiceList($value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- if (null !== $this->choiceList) {
- return $this->choiceList;
- }
-
- return $this->choiceList = new ArrayChoiceList(array_flip(Languages::getNames()), $value);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoicesForValues(array $values, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $values = array_filter($values);
- if (empty($values)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getChoicesForValues($values);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadValuesForChoices(array $choices, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $choices = array_filter($choices);
- if (empty($choices)) {
- return [];
- }
-
- // If no callable is set, choices are the same as values
- if (null === $value) {
- return $choices;
- }
-
- return $this->loadChoiceList($value)->getValuesForChoices($choices);
- }
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php
index 5ce654bb231f7..8c1c2890a0f2e 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php
@@ -12,28 +12,14 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
-use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
use Symfony\Component\Intl\Locales;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-class LocaleType extends AbstractType implements ChoiceLoaderInterface
+class LocaleType extends AbstractType
{
- /**
- * Locale loaded choice list.
- *
- * The choices are lazy loaded and generated from the Intl component.
- *
- * {@link \Symfony\Component\Intl\Intl::getLocaleBundle()}.
- *
- * @var ArrayChoiceList
- *
- * @deprecated since Symfony 4.1
- */
- private $choiceList;
-
/**
* {@inheritdoc}
*/
@@ -43,9 +29,9 @@ public function configureOptions(OptionsResolver $resolver)
'choice_loader' => function (Options $options) {
$choiceTranslationLocale = $options['choice_translation_locale'];
- return new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale) {
+ return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($choiceTranslationLocale) {
return array_flip(Locales::getNames($choiceTranslationLocale));
- });
+ }), $choiceTranslationLocale);
},
'choice_translation_domain' => false,
'choice_translation_locale' => null,
@@ -69,61 +55,4 @@ public function getBlockPrefix()
{
return 'locale';
}
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoiceList($value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- if (null !== $this->choiceList) {
- return $this->choiceList;
- }
-
- return $this->choiceList = new ArrayChoiceList(array_flip(Locales::getNames()), $value);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadChoicesForValues(array $values, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $values = array_filter($values);
- if (empty($values)) {
- return [];
- }
-
- return $this->loadChoiceList($value)->getChoicesForValues($values);
- }
-
- /**
- * {@inheritdoc}
- *
- * @deprecated since Symfony 4.1
- */
- public function loadValuesForChoices(array $choices, $value = null)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED);
-
- // Optimize
- $choices = array_filter($choices);
- if (empty($choices)) {
- return [];
- }
-
- // If no callable is set, choices are the same as values
- if (null === $value) {
- return $choices;
- }
-
- return $this->loadChoiceList($value)->getValuesForChoices($choices);
- }
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php b/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php
index 09a7383c23029..d402d311372b6 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php
@@ -87,7 +87,7 @@ public function getBlockPrefix()
* The pattern contains the placeholder "{{ widget }}" where the HTML tag should
* be inserted
*/
- protected static function getPattern($currency)
+ protected static function getPattern(?string $currency)
{
if (!$currency) {
return '{{ widget }}';
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php b/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php
index 0ee965d8c458f..4c1f1fd71f16b 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\StringToFloatTransformer;
@@ -38,19 +37,6 @@ public function buildForm(FormBuilderInterface $builder, array $options)
if ('string' === $options['input']) {
$builder->addModelTransformer(new StringToFloatTransformer($options['scale']));
- $builder->addModelTransformer(new CallbackTransformer(
- function ($value) {
- if (\is_float($value) || \is_int($value)) {
- @trigger_error(sprintf('Using the %s with float or int data when the "input" option is set to "string" is deprecated since Symfony 4.4 and will throw an exception in 5.0.', self::class), E_USER_DEPRECATED);
- $value = (string) $value;
- }
-
- return $value;
- },
- function ($value) {
- return $value;
- }
- ));
}
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/SearchType.php b/src/Symfony/Component/Form/Extension/Core/Type/SearchType.php
index c817a26d025b6..94ffffa5e6676 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/SearchType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/SearchType.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormView;
class SearchType extends AbstractType
{
@@ -23,6 +25,14 @@ public function getParent()
return TextType::class;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function buildView(FormView $view, FormInterface $form, array $options)
+ {
+ $view->vars['attr']['inputmode'] = $options['attr']['inputmode'] ?? 'search';
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TelType.php b/src/Symfony/Component/Form/Extension/Core/Type/TelType.php
index de74a3ed3721d..535dba8e1093d 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/TelType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/TelType.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormView;
class TelType extends AbstractType
{
@@ -23,6 +25,14 @@ public function getParent()
return TextType::class;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function buildView(FormView $view, FormInterface $form, array $options)
+ {
+ $view->vars['attr']['inputmode'] = $options['attr']['inputmode'] ?? 'tel';
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php
index 7475b09b2e451..cae3ae9350749 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php
@@ -13,6 +13,7 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Exception\InvalidConfigurationException;
+use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
@@ -293,6 +294,18 @@ public function configureOptions(OptionsResolver $resolver)
return null;
};
+ $viewTimezone = static function (Options $options, $value): ?string {
+ if (null !== $value) {
+ return $value;
+ }
+
+ if (null !== $options['model_timezone'] && null === $options['reference_date']) {
+ return $options['model_timezone'];
+ }
+
+ return null;
+ };
+
$resolver->setDefaults([
'hours' => range(0, 23),
'minutes' => range(0, 59),
@@ -303,7 +316,7 @@ public function configureOptions(OptionsResolver $resolver)
'with_minutes' => true,
'with_seconds' => false,
'model_timezone' => $modelTimezone,
- 'view_timezone' => null,
+ 'view_timezone' => $viewTimezone,
'reference_date' => null,
'placeholder' => $placeholderDefault,
'html5' => true,
@@ -323,12 +336,12 @@ public function configureOptions(OptionsResolver $resolver)
'choice_translation_domain' => false,
]);
- $resolver->setDeprecated('model_timezone', function (Options $options, $modelTimezone): string {
- if (null !== $modelTimezone && $options['view_timezone'] !== $modelTimezone && null === $options['reference_date']) {
- return sprintf('Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is deprecated since Symfony 4.4.');
+ $resolver->setNormalizer('view_timezone', function (Options $options, $viewTimezone): ?string {
+ if (null !== $options['model_timezone'] && $viewTimezone !== $options['model_timezone'] && null === $options['reference_date']) {
+ throw new LogicException(sprintf('Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is not supported.'));
}
- return '';
+ return $viewTimezone;
});
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php
index 53347946a0652..1aba449665a39 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
@@ -49,21 +49,18 @@ public function configureOptions(OptionsResolver $resolver)
if ($options['intl']) {
$choiceTranslationLocale = $options['choice_translation_locale'];
- return new IntlCallbackChoiceLoader(function () use ($input, $choiceTranslationLocale) {
+ return ChoiceList::loader($this, new IntlCallbackChoiceLoader(function () use ($input, $choiceTranslationLocale) {
return self::getIntlTimezones($input, $choiceTranslationLocale);
- });
+ }), [$input, $choiceTranslationLocale]);
}
- $regions = $options->offsetGet('regions', false);
-
- return new CallbackChoiceLoader(function () use ($regions, $input) {
- return self::getPhpTimezones($regions, $input);
- });
+ return ChoiceList::lazy($this, function () use ($input) {
+ return self::getPhpTimezones($input);
+ }, $input);
},
'choice_translation_domain' => false,
'choice_translation_locale' => null,
'input' => 'string',
- 'regions' => \DateTimeZone::ALL,
]);
$resolver->setAllowedTypes('intl', ['bool']);
@@ -85,16 +82,6 @@ public function configureOptions(OptionsResolver $resolver)
return $value;
});
-
- $resolver->setAllowedTypes('regions', 'int');
- $resolver->setDeprecated('regions', 'The option "%name%" is deprecated since Symfony 4.2.');
- $resolver->setNormalizer('regions', function (Options $options, $value) {
- if ($options['intl'] && \DateTimeZone::ALL !== (\DateTimeZone::ALL & $value)) {
- throw new LogicException('The "regions" option can only be used if the "intl" option is set to false.');
- }
-
- return $value;
- });
}
/**
@@ -113,11 +100,11 @@ public function getBlockPrefix()
return 'timezone';
}
- private static function getPhpTimezones(int $regions, string $input): array
+ private static function getPhpTimezones(string $input): array
{
$timezones = [];
- foreach (\DateTimeZone::listIdentifiers($regions) as $timezone) {
+ foreach (\DateTimeZone::listIdentifiers(\DateTimeZone::ALL) as $timezone) {
if ('intltimezone' === $input && 'Etc/Unknown' === \IntlTimeZone::createTimeZone($timezone)->getID()) {
continue;
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php b/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php
index 8cdca71b65ba1..f93586ef735ab 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php
@@ -14,7 +14,6 @@
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\EventListener\TransformationFailureListener;
use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -24,14 +23,8 @@ class TransformationFailureExtension extends AbstractTypeExtension
{
private $translator;
- /**
- * @param TranslatorInterface|null $translator
- */
- public function __construct($translator = null)
+ public function __construct(TranslatorInterface $translator = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->translator = $translator;
}
diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
index efd403ca9f689..609a371ea05d9 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
@@ -13,7 +13,6 @@
use Symfony\Component\Form\AbstractExtension;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -27,15 +26,8 @@ class CsrfExtension extends AbstractExtension
private $translator;
private $translationDomain;
- /**
- * @param TranslatorInterface|null $translator The translator for translating error messages
- * @param string|null $translationDomain The translation domain for translating
- */
- public function __construct(CsrfTokenManagerInterface $tokenManager, $translator = null, string $translationDomain = null)
+ public function __construct(CsrfTokenManagerInterface $tokenManager, TranslatorInterface $translator = null, string $translationDomain = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 2 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->tokenManager = $tokenManager;
$this->translator = $translator;
$this->translationDomain = $translationDomain;
diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
index 7c5181e17d9b8..d30bdef61e8e3 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
@@ -18,7 +18,6 @@
use Symfony\Component\Form\Util\ServerParams;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -41,14 +40,8 @@ public static function getSubscribedEvents()
];
}
- /**
- * @param TranslatorInterface|null $translator
- */
- public function __construct(string $fieldName, CsrfTokenManagerInterface $tokenManager, string $tokenId, string $errorMessage, $translator = null, string $translationDomain = null, ServerParams $serverParams = null)
+ public function __construct(string $fieldName, CsrfTokenManagerInterface $tokenManager, string $tokenId, string $errorMessage, TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 5 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->fieldName = $fieldName;
$this->tokenManager = $tokenManager;
$this->tokenId = $tokenId;
diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
index 2f3fa437f6dda..5d991e616414d 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
@@ -20,7 +20,6 @@
use Symfony\Component\Form\Util\ServerParams;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -35,14 +34,8 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
private $translationDomain;
private $serverParams;
- /**
- * @param TranslatorInterface|null $translator
- */
- public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', $translator = null, string $translationDomain = null, ServerParams $serverParams = null)
+ public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null)
{
- if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 4 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->defaultTokenManager = $defaultTokenManager;
$this->defaultEnabled = $defaultEnabled;
$this->defaultFieldName = $defaultFieldName;
diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
index 4d4a7b125189a..969fcd066f764 100644
--- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
+++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php
@@ -28,7 +28,7 @@
* @author Robert Schönthal
* @author Bernhard Schussek
*
- * @final since Symfony 4.3
+ * @final
*/
class FormDataCollector extends DataCollector implements FormDataCollectorInterface
{
@@ -80,12 +80,8 @@ public function __construct(FormDataExtractorInterface $dataExtractor)
/**
* Does nothing. The data is collected during the form event listeners.
- *
- * {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
}
@@ -223,7 +219,7 @@ public function buildFinalFormTree(FormInterface $form, FormView $view)
/**
* {@inheritdoc}
*/
- public function getName()
+ public function getName(): string
{
return 'form';
}
@@ -255,7 +251,7 @@ public function __sleep(): array
/**
* {@inheritdoc}
*/
- protected function getCasters()
+ protected function getCasters(): array
{
return parent::getCasters() + [
\Exception::class => function (\Exception $e, array $a, Stub $s) {
diff --git a/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php b/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php
index 53a76c8490d79..54358d5d49aa1 100644
--- a/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php
+++ b/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php
@@ -69,7 +69,7 @@ public function getTypeExtensions()
/**
* {@inheritdoc}
*/
- public function createBuilder(FormFactoryInterface $factory, $name, array $options = [])
+ public function createBuilder(FormFactoryInterface $factory, string $name, array $options = [])
{
$builder = $this->proxiedType->createBuilder($factory, $name, $options);
diff --git a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
index e4314648df8d4..94e6bd01686f6 100644
--- a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
+++ b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
@@ -34,7 +34,10 @@ public function __construct(ContainerInterface $typeContainer, array $typeExtens
$this->guesserServices = $guesserServices;
}
- public function getType($name)
+ /**
+ * {@inheritdoc}
+ */
+ public function getType(string $name)
{
if (!$this->typeContainer->has($name)) {
throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name));
@@ -43,12 +46,18 @@ public function getType($name)
return $this->typeContainer->get($name);
}
- public function hasType($name)
+ /**
+ * {@inheritdoc}
+ */
+ public function hasType(string $name)
{
return $this->typeContainer->has($name);
}
- public function getTypeExtensions($name)
+ /**
+ * {@inheritdoc}
+ */
+ public function getTypeExtensions(string $name)
{
$extensions = [];
@@ -56,17 +65,12 @@ public function getTypeExtensions($name)
foreach ($this->typeExtensionServices[$name] as $serviceId => $extension) {
$extensions[] = $extension;
- if (method_exists($extension, 'getExtendedTypes')) {
- $extendedTypes = [];
-
- foreach ($extension::getExtendedTypes() as $extendedType) {
- $extendedTypes[] = $extendedType;
- }
- } else {
- $extendedTypes = [$extension->getExtendedType()];
+ $extendedTypes = [];
+ foreach ($extension::getExtendedTypes() as $extendedType) {
+ $extendedTypes[] = $extendedType;
}
- // validate the result of getExtendedTypes()/getExtendedType() to ensure it is consistent with the service definition
+ // validate the result of getExtendedTypes() to ensure it is consistent with the service definition
if (!\in_array($name, $extendedTypes, true)) {
throw new InvalidArgumentException(sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s".', $serviceId, $name, implode(', ', $extendedTypes)));
}
@@ -76,11 +80,17 @@ public function getTypeExtensions($name)
return $extensions;
}
- public function hasTypeExtensions($name)
+ /**
+ * {@inheritdoc}
+ */
+ public function hasTypeExtensions(string $name)
{
return isset($this->typeExtensionServices[$name]);
}
+ /**
+ * {@inheritdoc}
+ */
public function getTypeGuesser()
{
if (!$this->guesserLoaded) {
diff --git a/src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php b/src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php
deleted file mode 100644
index a510752e11914..0000000000000
--- a/src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php
+++ /dev/null
@@ -1,37 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Extension\Templating;
-
-@trigger_error('The '.TemplatingExtension::class.' class is deprecated since version 4.3 and will be removed in 5.0; use Twig instead.', E_USER_DEPRECATED);
-
-use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper;
-use Symfony\Component\Form\AbstractExtension;
-use Symfony\Component\Form\FormRenderer;
-use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
-use Symfony\Component\Templating\PhpEngine;
-
-/**
- * Integrates the Templating component with the Form library.
- *
- * @author Bernhard Schussek
- *
- * @deprecated since version 4.3, to be removed in 5.0; use Twig instead.
- */
-class TemplatingExtension extends AbstractExtension
-{
- public function __construct(PhpEngine $engine, CsrfTokenManagerInterface $csrfTokenManager = null, array $defaultThemes = [])
- {
- $engine->addHelpers([
- new FormHelper(new FormRenderer(new TemplatingRendererEngine($engine, $defaultThemes), $csrfTokenManager)),
- ]);
- }
-}
diff --git a/src/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php b/src/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php
deleted file mode 100644
index 1316e6a165a6d..0000000000000
--- a/src/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php
+++ /dev/null
@@ -1,128 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Extension\Templating;
-
-@trigger_error('The '.TemplatingRendererEngine::class.' class is deprecated since version 4.3 and will be removed in 5.0; use Twig instead.', E_USER_DEPRECATED);
-
-use Symfony\Component\Form\AbstractRendererEngine;
-use Symfony\Component\Form\FormView;
-use Symfony\Component\Templating\EngineInterface;
-
-/**
- * @author Bernhard Schussek
- *
- * @deprecated since version 4.3, to be removed in 5.0; use Twig instead.
- */
-class TemplatingRendererEngine extends AbstractRendererEngine
-{
- private $engine;
-
- public function __construct(EngineInterface $engine, array $defaultThemes = [])
- {
- parent::__construct($defaultThemes);
-
- $this->engine = $engine;
- }
-
- /**
- * {@inheritdoc}
- */
- public function renderBlock(FormView $view, $resource, $blockName, array $variables = [])
- {
- return trim($this->engine->render($resource, $variables));
- }
-
- /**
- * Loads the cache with the resource for a given block name.
- *
- * This implementation tries to load as few blocks as possible, since each block
- * is represented by a template on the file system.
- *
- * @see getResourceForBlock()
- *
- * @param string $cacheKey The cache key of the form view
- * @param FormView $view The form view for finding the applying themes
- * @param string $blockName The name of the block to load
- *
- * @return bool True if the resource could be loaded, false otherwise
- */
- protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName)
- {
- // Recursively try to find the block in the themes assigned to $view,
- // then of its parent form, then of the parent form of the parent and so on.
- // When the root form is reached in this recursion, also the default
- // themes are taken into account.
-
- // Check each theme whether it contains the searched block
- if (isset($this->themes[$cacheKey])) {
- for ($i = \count($this->themes[$cacheKey]) - 1; $i >= 0; --$i) {
- if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->themes[$cacheKey][$i])) {
- return true;
- }
- }
- }
-
- // Check the default themes once we reach the root form without success
- if (!$view->parent) {
- if (!isset($this->useDefaultThemes[$cacheKey]) || $this->useDefaultThemes[$cacheKey]) {
- for ($i = \count($this->defaultThemes) - 1; $i >= 0; --$i) {
- if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->defaultThemes[$i])) {
- return true;
- }
- }
- }
- }
-
- // If we did not find anything in the themes of the current view, proceed
- // with the themes of the parent view
- if ($view->parent) {
- $parentCacheKey = $view->parent->vars[self::CACHE_KEY_VAR];
-
- if (!isset($this->resources[$parentCacheKey][$blockName])) {
- $this->loadResourceForBlockName($parentCacheKey, $view->parent, $blockName);
- }
-
- // If a template exists in the parent themes, cache that template
- // for the current theme as well to speed up further accesses
- if ($this->resources[$parentCacheKey][$blockName]) {
- $this->resources[$cacheKey][$blockName] = $this->resources[$parentCacheKey][$blockName];
-
- return true;
- }
- }
-
- // Cache that we didn't find anything to speed up further accesses
- $this->resources[$cacheKey][$blockName] = false;
-
- return false;
- }
-
- /**
- * Tries to load the resource for a block from a theme.
- *
- * @param string $cacheKey The cache key for storing the resource
- * @param string $blockName The name of the block to load a resource for
- * @param mixed $theme The theme to load the block from
- *
- * @return bool True if the resource could be loaded, false otherwise
- */
- protected function loadResourceFromTheme($cacheKey, $blockName, $theme)
- {
- if ($this->engine->exists($templateName = $theme.':'.$blockName.'.html.php')) {
- $this->resources[$cacheKey][$blockName] = $templateName;
-
- return true;
- }
-
- return false;
- }
-}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
index c5b71f903c16d..21e4fe20eaf69 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
@@ -15,7 +15,6 @@
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -27,14 +26,8 @@ class UploadValidatorExtension extends AbstractTypeExtension
private $translator;
private $translationDomain;
- /**
- * @param TranslatorInterface $translator
- */
- public function __construct($translator, string $translationDomain = null)
+ public function __construct(TranslatorInterface $translator, string $translationDomain = null)
{
- if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
- throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
- }
$this->translator = $translator;
$this->translationDomain = $translationDomain;
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
index 4e0443dd3b680..2dbe47851d6b8 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
@@ -31,7 +31,7 @@ public function __construct(MetadataFactoryInterface $metadataFactory)
/**
* {@inheritdoc}
*/
- public function guessType($class, $property)
+ public function guessType(string $class, string $property)
{
return $this->guess($class, $property, function (Constraint $constraint) {
return $this->guessTypeForConstraint($constraint);
@@ -41,7 +41,7 @@ public function guessType($class, $property)
/**
* {@inheritdoc}
*/
- public function guessRequired($class, $property)
+ public function guessRequired(string $class, string $property)
{
return $this->guess($class, $property, function (Constraint $constraint) {
return $this->guessRequiredForConstraint($constraint);
@@ -53,7 +53,7 @@ public function guessRequired($class, $property)
/**
* {@inheritdoc}
*/
- public function guessMaxLength($class, $property)
+ public function guessMaxLength(string $class, string $property)
{
return $this->guess($class, $property, function (Constraint $constraint) {
return $this->guessMaxLengthForConstraint($constraint);
@@ -63,7 +63,7 @@ public function guessMaxLength($class, $property)
/**
* {@inheritdoc}
*/
- public function guessPattern($class, $property)
+ public function guessPattern(string $class, string $property)
{
return $this->guess($class, $property, function (Constraint $constraint) {
return $this->guessPatternForConstraint($constraint);
@@ -251,8 +251,6 @@ public function guessPatternForConstraint(Constraint $constraint)
* Iterates over the constraints of a property, executes a constraints on
* them and returns the best guess.
*
- * @param string $class The class to read the constraints from
- * @param string $property The property for which to find constraints
* @param \Closure $closure The closure that returns a guess
* for a given constraint
* @param mixed $defaultValue The default value assumed if no other value
@@ -260,7 +258,7 @@ public function guessPatternForConstraint(Constraint $constraint)
*
* @return Guess|null The guessed value with the highest confidence
*/
- protected function guess($class, $property, \Closure $closure, $defaultValue = null)
+ protected function guess(string $class, string $property, \Closure $closure, $defaultValue = null)
{
$guesses = [];
$classMetadata = $this->metadataFactory->getMetadataFor($class);
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
index 78be91c8b8ba3..1891be0c441e6 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php
@@ -44,11 +44,9 @@ public function getOrigin()
* If the rule matches, the form mapped by the rule is returned.
* Otherwise this method returns false.
*
- * @param string $propertyPath The property path to match against the rule
- *
* @return FormInterface|null The mapped form or null
*/
- public function match($propertyPath)
+ public function match(string $propertyPath)
{
return $propertyPath === $this->propertyPath ? $this->getTarget() : null;
}
@@ -56,11 +54,9 @@ public function match($propertyPath)
/**
* Matches a property path against a prefix of the rule path.
*
- * @param string $propertyPath The property path to match against the rule
- *
* @return bool Whether the property path is a prefix of the rule or not
*/
- public function isPrefix($propertyPath)
+ public function isPrefix(string $propertyPath)
{
$length = \strlen($propertyPath);
$prefix = substr($this->propertyPath, 0, $length);
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
index 15fd049372002..22ec19c9945eb 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
@@ -32,7 +32,7 @@ class ViolationMapper implements ViolationMapperInterface
/**
* {@inheritdoc}
*/
- public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false)
+ public function mapViolation(ConstraintViolation $violation, FormInterface $form, bool $allowNonSynchronized = false)
{
$this->allowNonSynchronized = $allowNonSynchronized;
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php
index b17abc2cc183e..57ed1c84ce909 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php
@@ -25,5 +25,5 @@ interface ViolationMapperInterface
*
* @param bool $allowNonSynchronized Whether to allow mapping to non-synchronized forms
*/
- public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false);
+ public function mapViolation(ConstraintViolation $violation, FormInterface $form, bool $allowNonSynchronized = false);
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
index 343cd6dae6bfd..7c0f1631ddcee 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
@@ -160,7 +160,7 @@ public function getElements()
/**
* {@inheritdoc}
*/
- public function getElement($index)
+ public function getElement(int $index)
{
if (!isset($this->elements[$index])) {
throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
@@ -172,7 +172,7 @@ public function getElement($index)
/**
* {@inheritdoc}
*/
- public function isProperty($index)
+ public function isProperty(int $index)
{
if (!isset($this->isIndex[$index])) {
throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
@@ -184,7 +184,7 @@ public function isProperty($index)
/**
* {@inheritdoc}
*/
- public function isIndex($index)
+ public function isIndex(int $index)
{
if (!isset($this->isIndex[$index])) {
throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
@@ -203,13 +203,11 @@ public function isIndex($index)
* In this example, "address" and "office" map to forms, while
* "street does not.
*
- * @param int $index The element index
- *
* @return bool Whether the element maps to a form
*
* @throws OutOfBoundsException if the offset is invalid
*/
- public function mapsForm($index)
+ public function mapsForm(int $index)
{
if (!isset($this->mapsForm[$index])) {
throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php
index c4f0413dd350c..c933214ccf91f 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Form;
-use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
use Symfony\Component\Form\Event\PostSetDataEvent;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Event\PreSetDataEvent;
@@ -334,7 +333,7 @@ public function setData($modelData)
}
$this->lockSetData = true;
- $dispatcher = LegacyEventDispatcherProxy::decorate($this->config->getEventDispatcher());
+ $dispatcher = $this->config->getEventDispatcher();
// Hook to change content of the model data before transformation and mapping children
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
@@ -498,7 +497,7 @@ public function handleRequest($request = null)
/**
* {@inheritdoc}
*/
- public function submit($submittedData, $clearMissing = true)
+ public function submit($submittedData, bool $clearMissing = true)
{
if ($this->submitted) {
throw new AlreadySubmittedException('A form can only be submitted once');
@@ -541,7 +540,7 @@ public function submit($submittedData, $clearMissing = true)
$this->transformationFailure = new TransformationFailedException('Submitted data was expected to be text or number, array given.');
}
- $dispatcher = LegacyEventDispatcherProxy::decorate($this->config->getEventDispatcher());
+ $dispatcher = $this->config->getEventDispatcher();
$modelData = null;
$normData = null;
@@ -727,6 +726,18 @@ public function isEmpty()
}
}
+ if (!method_exists($this->config, 'getIsEmptyCallback')) {
+ trigger_deprecation('symfony/form', '5.1', 'Not implementing the "%s::getIsEmptyCallback()" method in "%s" is deprecated.', FormConfigInterface::class, \get_class($this->config));
+
+ $isEmptyCallback = null;
+ } else {
+ $isEmptyCallback = $this->config->getIsEmptyCallback();
+ }
+
+ if (null !== $isEmptyCallback) {
+ return $isEmptyCallback($this->modelData);
+ }
+
return FormUtil::isEmpty($this->modelData) ||
// arrays, countables
((\is_array($this->modelData) || $this->modelData instanceof \Countable) && 0 === \count($this->modelData)) ||
@@ -767,7 +778,7 @@ public function getClickedButton()
/**
* {@inheritdoc}
*/
- public function getErrors($deep = false, $flatten = true)
+ public function getErrors(bool $deep = false, bool $flatten = true)
{
$errors = $this->errors;
@@ -830,7 +841,7 @@ public function all()
/**
* {@inheritdoc}
*/
- public function add($child, $type = null, array $options = [])
+ public function add($child, string $type = null, array $options = [])
{
if ($this->submitted) {
throw new AlreadySubmittedException('You cannot add children to a submitted form');
@@ -901,7 +912,7 @@ public function add($child, $type = null, array $options = [])
/**
* {@inheritdoc}
*/
- public function remove($name)
+ public function remove(string $name)
{
if ($this->submitted) {
throw new AlreadySubmittedException('You cannot remove children from a submitted form');
@@ -921,7 +932,7 @@ public function remove($name)
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
return isset($this->children[$name]);
}
@@ -929,7 +940,7 @@ public function has($name)
/**
* {@inheritdoc}
*/
- public function get($name)
+ public function get(string $name)
{
if (isset($this->children[$name])) {
return $this->children[$name];
diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php
index e9c7213a98944..53fbcb51963c2 100644
--- a/src/Symfony/Component/Form/FormBuilder.php
+++ b/src/Symfony/Component/Form/FormBuilder.php
@@ -47,7 +47,7 @@ public function __construct(?string $name, ?string $dataClass, EventDispatcherIn
/**
* {@inheritdoc}
*/
- public function add($child, $type = null, array $options = [])
+ public function add($child, string $type = null, array $options = [])
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
@@ -80,7 +80,7 @@ public function add($child, $type = null, array $options = [])
/**
* {@inheritdoc}
*/
- public function create($name, $type = null, array $options = [])
+ public function create($name, string $type = null, array $options = [])
{
if ($this->locked) {
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
diff --git a/src/Symfony/Component/Form/FormBuilderInterface.php b/src/Symfony/Component/Form/FormBuilderInterface.php
index 902fa0f9505e5..05c565bb6b472 100644
--- a/src/Symfony/Component/Form/FormBuilderInterface.php
+++ b/src/Symfony/Component/Form/FormBuilderInterface.php
@@ -24,11 +24,10 @@ interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuild
* object hierarchy.
*
* @param string|FormBuilderInterface $child
- * @param string|null $type
*
* @return self
*/
- public function add($child, $type = null, array $options = []);
+ public function add($child, string $type = null, array $options = []);
/**
* Creates a form builder.
@@ -38,36 +37,30 @@ public function add($child, $type = null, array $options = []);
*
* @return self
*/
- public function create($name, $type = null, array $options = []);
+ public function create(string $name, string $type = null, array $options = []);
/**
* Returns a child by name.
*
- * @param string $name The name of the child
- *
* @return self
*
* @throws Exception\InvalidArgumentException if the given child does not exist
*/
- public function get($name);
+ public function get(string $name);
/**
* Removes the field with the given name.
*
- * @param string $name
- *
* @return self
*/
- public function remove($name);
+ public function remove(string $name);
/**
* Returns whether a field with the given name exists.
*
- * @param string $name
- *
* @return bool
*/
- public function has($name);
+ public function has(string $name);
/**
* Returns the children.
diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php
index dab8bd56cb2ea..7e7b40edacabb 100644
--- a/src/Symfony/Component/Form/FormConfigBuilder.php
+++ b/src/Symfony/Component/Form/FormConfigBuilder.php
@@ -16,7 +16,6 @@
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
use Symfony\Component\Form\Exception\BadMethodCallException;
use Symfony\Component\Form\Exception\InvalidArgumentException;
-use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\PropertyAccess\PropertyPathInterface;
@@ -103,6 +102,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
private $autoInitialize = false;
private $options;
+ private $isEmptyCallback;
/**
* Creates an empty form configuration.
@@ -130,7 +130,7 @@ public function __construct(?string $name, ?string $dataClass, EventDispatcherIn
/**
* {@inheritdoc}
*/
- public function addEventListener($eventName, $listener, $priority = 0)
+ public function addEventListener(string $eventName, callable $listener, int $priority = 0)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
@@ -158,7 +158,7 @@ public function addEventSubscriber(EventSubscriberInterface $subscriber)
/**
* {@inheritdoc}
*/
- public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false)
+ public function addViewTransformer(DataTransformerInterface $viewTransformer, bool $forcePrepend = false)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
@@ -190,7 +190,7 @@ public function resetViewTransformers()
/**
* {@inheritdoc}
*/
- public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false)
+ public function addModelTransformer(DataTransformerInterface $modelTransformer, bool $forceAppend = false)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
@@ -354,7 +354,7 @@ public function getAttributes()
/**
* {@inheritdoc}
*/
- public function hasAttribute($name)
+ public function hasAttribute(string $name)
{
return \array_key_exists($name, $this->attributes);
}
@@ -362,7 +362,7 @@ public function hasAttribute($name)
/**
* {@inheritdoc}
*/
- public function getAttribute($name, $default = null)
+ public function getAttribute(string $name, $default = null)
{
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
@@ -449,7 +449,7 @@ public function getOptions()
/**
* {@inheritdoc}
*/
- public function hasOption($name)
+ public function hasOption(string $name)
{
return \array_key_exists($name, $this->options);
}
@@ -457,7 +457,7 @@ public function hasOption($name)
/**
* {@inheritdoc}
*/
- public function getOption($name, $default = null)
+ public function getOption(string $name, $default = null)
{
return \array_key_exists($name, $this->options) ? $this->options[$name] : $default;
}
@@ -465,7 +465,15 @@ public function getOption($name, $default = null)
/**
* {@inheritdoc}
*/
- public function setAttribute($name, $value)
+ public function getIsEmptyCallback(): ?callable
+ {
+ return $this->isEmptyCallback;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAttribute(string $name, $value)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
@@ -507,13 +515,13 @@ public function setDataMapper(DataMapperInterface $dataMapper = null)
/**
* {@inheritdoc}
*/
- public function setDisabled($disabled)
+ public function setDisabled(bool $disabled)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->disabled = (bool) $disabled;
+ $this->disabled = $disabled;
return $this;
}
@@ -535,13 +543,13 @@ public function setEmptyData($emptyData)
/**
* {@inheritdoc}
*/
- public function setErrorBubbling($errorBubbling)
+ public function setErrorBubbling(bool $errorBubbling)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->errorBubbling = (bool) $errorBubbling;
+ $this->errorBubbling = $errorBubbling;
return $this;
}
@@ -549,13 +557,13 @@ public function setErrorBubbling($errorBubbling)
/**
* {@inheritdoc}
*/
- public function setRequired($required)
+ public function setRequired(bool $required)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->required = (bool) $required;
+ $this->required = $required;
return $this;
}
@@ -581,13 +589,13 @@ public function setPropertyPath($propertyPath)
/**
* {@inheritdoc}
*/
- public function setMapped($mapped)
+ public function setMapped(bool $mapped)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->mapped = (bool) $mapped;
+ $this->mapped = $mapped;
return $this;
}
@@ -595,13 +603,13 @@ public function setMapped($mapped)
/**
* {@inheritdoc}
*/
- public function setByReference($byReference)
+ public function setByReference(bool $byReference)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->byReference = (bool) $byReference;
+ $this->byReference = $byReference;
return $this;
}
@@ -609,13 +617,13 @@ public function setByReference($byReference)
/**
* {@inheritdoc}
*/
- public function setInheritData($inheritData)
+ public function setInheritData(bool $inheritData)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->inheritData = (bool) $inheritData;
+ $this->inheritData = $inheritData;
return $this;
}
@@ -623,13 +631,13 @@ public function setInheritData($inheritData)
/**
* {@inheritdoc}
*/
- public function setCompound($compound)
+ public function setCompound(bool $compound)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->compound = (bool) $compound;
+ $this->compound = $compound;
return $this;
}
@@ -665,13 +673,13 @@ public function setData($data)
/**
* {@inheritdoc}
*/
- public function setDataLocked($locked)
+ public function setDataLocked(bool $locked)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->dataLocked = (bool) $locked;
+ $this->dataLocked = $locked;
return $this;
}
@@ -693,13 +701,13 @@ public function setFormFactory(FormFactoryInterface $formFactory)
/**
* {@inheritdoc}
*/
- public function setAction($action)
+ public function setAction(string $action)
{
if ($this->locked) {
throw new BadMethodCallException('The config builder cannot be modified anymore.');
}
- $this->action = (string) $action;
+ $this->action = $action;
return $this;
}
@@ -707,7 +715,7 @@ public function setAction($action)
/**
* {@inheritdoc}
*/
- public function setMethod($method)
+ public function setMethod(string $method)
{
if ($this->locked) {
throw new BadMethodCallException('The config builder cannot be modified anymore.');
@@ -735,13 +743,13 @@ public function setRequestHandler(RequestHandlerInterface $requestHandler)
/**
* {@inheritdoc}
*/
- public function setAutoInitialize($initialize)
+ public function setAutoInitialize(bool $initialize)
{
if ($this->locked) {
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- $this->autoInitialize = (bool) $initialize;
+ $this->autoInitialize = $initialize;
return $this;
}
@@ -762,22 +770,25 @@ public function getFormConfig()
return $config;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setIsEmptyCallback(?callable $isEmptyCallback)
+ {
+ $this->isEmptyCallback = $isEmptyCallback;
+
+ return $this;
+ }
+
/**
* Validates whether the given variable is a valid form name.
*
- * @param string|null $name The tested form name
- *
- * @throws UnexpectedTypeException if the name is not a string or an integer
* @throws InvalidArgumentException if the name contains invalid characters
*
- * @internal since Symfony 4.4
+ * @internal
*/
- public static function validateName($name)
+ final public static function validateName(?string $name)
{
- if (null !== $name && !\is_string($name) && !\is_int($name)) {
- throw new UnexpectedTypeException($name, 'string or null');
- }
-
if (!self::isValidName($name)) {
throw new InvalidArgumentException(sprintf('The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").', $name));
}
@@ -792,10 +803,8 @@ public static function validateName($name)
* * starts with a letter, digit or underscore
* * contains only letters, digits, numbers, underscores ("_"),
* hyphens ("-") and colons (":")
- *
- * @final since Symfony 4.4
*/
- public static function isValidName(?string $name): bool
+ final public static function isValidName(?string $name): bool
{
return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name);
}
diff --git a/src/Symfony/Component/Form/FormConfigBuilderInterface.php b/src/Symfony/Component/Form/FormConfigBuilderInterface.php
index 59da9520ba109..d9064c1434a00 100644
--- a/src/Symfony/Component/Form/FormConfigBuilderInterface.php
+++ b/src/Symfony/Component/Form/FormConfigBuilderInterface.php
@@ -16,21 +16,21 @@
/**
* @author Bernhard Schussek
+ *
+ * @method $this setIsEmptyCallback(callable|null $isEmptyCallback) Sets the callback that will be called to determine if the model data of the form is empty or not - not implementing it is deprecated since Symfony 5.1
*/
interface FormConfigBuilderInterface extends FormConfigInterface
{
/**
* Adds an event listener to an event on this form.
*
- * @param string $eventName The name of the event to listen to
- * @param callable $listener The listener to execute
- * @param int $priority The priority of the listener. Listeners
- * with a higher priority are called before
- * listeners with a lower priority.
+ * @param int $priority The priority of the listener. Listeners
+ * with a higher priority are called before
+ * listeners with a lower priority.
*
* @return $this The configuration object
*/
- public function addEventListener($eventName, $listener, $priority = 0);
+ public function addEventListener(string $eventName, callable $listener, int $priority = 0);
/**
* Adds an event subscriber for events on this form.
@@ -51,7 +51,7 @@ public function addEventSubscriber(EventSubscriberInterface $subscriber);
*
* @return $this The configuration object
*/
- public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false);
+ public function addViewTransformer(DataTransformerInterface $viewTransformer, bool $forcePrepend = false);
/**
* Clears the view transformers.
@@ -72,7 +72,7 @@ public function resetViewTransformers();
*
* @return $this The configuration object
*/
- public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false);
+ public function addModelTransformer(DataTransformerInterface $modelTransformer, bool $forceAppend = false);
/**
* Clears the normalization transformers.
@@ -84,12 +84,11 @@ public function resetModelTransformers();
/**
* Sets the value for an attribute.
*
- * @param string $name The name of the attribute
- * @param mixed $value The value of the attribute
+ * @param mixed $value The value of the attribute
*
* @return $this The configuration object
*/
- public function setAttribute($name, $value);
+ public function setAttribute(string $name, $value);
/**
* Sets the attributes.
@@ -108,11 +107,9 @@ public function setDataMapper(DataMapperInterface $dataMapper = null);
/**
* Sets whether the form is disabled.
*
- * @param bool $disabled Whether the form is disabled
- *
* @return $this The configuration object
*/
- public function setDisabled($disabled);
+ public function setDisabled(bool $disabled);
/**
* Sets the data used for the client data when no value is submitted.
@@ -126,20 +123,16 @@ public function setEmptyData($emptyData);
/**
* Sets whether errors bubble up to the parent.
*
- * @param bool $errorBubbling
- *
* @return $this The configuration object
*/
- public function setErrorBubbling($errorBubbling);
+ public function setErrorBubbling(bool $errorBubbling);
/**
* Sets whether this field is required to be filled out when submitted.
*
- * @param bool $required
- *
* @return $this The configuration object
*/
- public function setRequired($required);
+ public function setRequired(bool $required);
/**
* Sets the property path that the form should be mapped to.
@@ -155,40 +148,32 @@ public function setPropertyPath($propertyPath);
* Sets whether the form should be mapped to an element of its
* parent's data.
*
- * @param bool $mapped Whether the form should be mapped
- *
* @return $this The configuration object
*/
- public function setMapped($mapped);
+ public function setMapped(bool $mapped);
/**
* Sets whether the form's data should be modified by reference.
*
- * @param bool $byReference Whether the data should be modified by reference
- *
* @return $this The configuration object
*/
- public function setByReference($byReference);
+ public function setByReference(bool $byReference);
/**
* Sets whether the form should read and write the data of its parent.
*
- * @param bool $inheritData Whether the form should inherit its parent's data
- *
* @return $this The configuration object
*/
- public function setInheritData($inheritData);
+ public function setInheritData(bool $inheritData);
/**
* Sets whether the form should be compound.
*
- * @param bool $compound Whether the form should be compound
- *
* @return $this The configuration object
*
* @see FormConfigInterface::getCompound()
*/
- public function setCompound($compound);
+ public function setCompound(bool $compound);
/**
* Sets the resolved type.
@@ -216,11 +201,9 @@ public function setData($data);
* It means data passed to a factory method or mapped from the
* parent will be ignored.
*
- * @param bool $locked Whether to lock the default configured data
- *
* @return $this The configuration object
*/
- public function setDataLocked($locked);
+ public function setDataLocked(bool $locked);
/**
* Sets the form factory used for creating new forms.
@@ -230,20 +213,16 @@ public function setFormFactory(FormFactoryInterface $formFactory);
/**
* Sets the target URL of the form.
*
- * @param string $action The target URL of the form
- *
* @return $this The configuration object
*/
- public function setAction($action);
+ public function setAction(string $action);
/**
* Sets the HTTP method used by the form.
*
- * @param string $method The HTTP method of the form
- *
* @return $this The configuration object
*/
- public function setMethod($method);
+ public function setMethod(string $method);
/**
* Sets the request handler used by the form.
@@ -264,7 +243,7 @@ public function setRequestHandler(RequestHandlerInterface $requestHandler);
*
* @return $this The configuration object
*/
- public function setAutoInitialize($initialize);
+ public function setAutoInitialize(bool $initialize);
/**
* Builds and returns the form configuration.
diff --git a/src/Symfony/Component/Form/FormConfigInterface.php b/src/Symfony/Component/Form/FormConfigInterface.php
index 7dbda33033b55..e76986c4fb6b7 100644
--- a/src/Symfony/Component/Form/FormConfigInterface.php
+++ b/src/Symfony/Component/Form/FormConfigInterface.php
@@ -18,6 +18,8 @@
* The configuration of a {@link Form} object.
*
* @author Bernhard Schussek
+ *
+ * @method callable|null getIsEmptyCallback() Returns a callable that takes the model data as argument and that returns if it is empty or not - not implementing it is deprecated since Symfony 5.1
*/
interface FormConfigInterface
{
@@ -149,21 +151,18 @@ public function getAttributes();
/**
* Returns whether the attribute with the given name exists.
*
- * @param string $name The attribute name
- *
* @return bool Whether the attribute exists
*/
- public function hasAttribute($name);
+ public function hasAttribute(string $name);
/**
* Returns the value of the given attribute.
*
- * @param string $name The attribute name
- * @param mixed $default The value returned if the attribute does not exist
+ * @param mixed $default The value returned if the attribute does not exist
*
* @return mixed The attribute value
*/
- public function getAttribute($name, $default = null);
+ public function getAttribute(string $name, $default = null);
/**
* Returns the initial data of the form.
@@ -236,19 +235,16 @@ public function getOptions();
/**
* Returns whether a specific option exists.
*
- * @param string $name The option name,
- *
* @return bool Whether the option exists
*/
- public function hasOption($name);
+ public function hasOption(string $name);
/**
* Returns the value of a specific option.
*
- * @param string $name The option name
- * @param mixed $default The value returned if the option does not exist
+ * @param mixed $default The value returned if the option does not exist
*
* @return mixed The option value
*/
- public function getOption($name, $default = null);
+ public function getOption(string $name, $default = null);
}
diff --git a/src/Symfony/Component/Form/FormError.php b/src/Symfony/Component/Form/FormError.php
index 10ff0961a6004..8ea67c4412558 100644
--- a/src/Symfony/Component/Form/FormError.php
+++ b/src/Symfony/Component/Form/FormError.php
@@ -47,13 +47,8 @@ class FormError
*
* @see \Symfony\Component\Translation\Translator
*/
- public function __construct(?string $message, string $messageTemplate = null, array $messageParameters = [], int $messagePluralization = null, $cause = null)
+ public function __construct(string $message, string $messageTemplate = null, array $messageParameters = [], int $messagePluralization = null, $cause = null)
{
- if (null === $message) {
- @trigger_error(sprintf('Passing a null message when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED);
- $message = '';
- }
-
$this->message = $message;
$this->messageTemplate = $messageTemplate ?: $message;
$this->messageParameters = $messageParameters;
diff --git a/src/Symfony/Component/Form/FormEvent.php b/src/Symfony/Component/Form/FormEvent.php
index 447774606519e..c466fafdc6d9e 100644
--- a/src/Symfony/Component/Form/FormEvent.php
+++ b/src/Symfony/Component/Form/FormEvent.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Form;
-use Symfony\Component\EventDispatcher\Event;
+use Symfony\Contracts\EventDispatcher\Event;
/**
* @author Bernhard Schussek
diff --git a/src/Symfony/Component/Form/FormExtensionInterface.php b/src/Symfony/Component/Form/FormExtensionInterface.php
index 1449b1f1e78cf..c2c119c54865a 100644
--- a/src/Symfony/Component/Form/FormExtensionInterface.php
+++ b/src/Symfony/Component/Form/FormExtensionInterface.php
@@ -25,7 +25,7 @@ interface FormExtensionInterface
*
* @throws Exception\InvalidArgumentException if the given type is not supported by this extension
*/
- public function getType($name);
+ public function getType(string $name);
/**
* Returns whether the given type is supported.
@@ -34,7 +34,7 @@ public function getType($name);
*
* @return bool Whether the type is supported by this extension
*/
- public function hasType($name);
+ public function hasType(string $name);
/**
* Returns the extensions for the given type.
@@ -43,7 +43,7 @@ public function hasType($name);
*
* @return FormTypeExtensionInterface[] An array of extensions as FormTypeExtensionInterface instances
*/
- public function getTypeExtensions($name);
+ public function getTypeExtensions(string $name);
/**
* Returns whether this extension provides type extensions for the given type.
@@ -52,7 +52,7 @@ public function getTypeExtensions($name);
*
* @return bool Whether the given type has extensions
*/
- public function hasTypeExtensions($name);
+ public function hasTypeExtensions(string $name);
/**
* Returns the type guesser provided by this extension.
diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php
index ac38b0a39e647..5e7e807333045 100644
--- a/src/Symfony/Component/Form/FormFactory.php
+++ b/src/Symfony/Component/Form/FormFactory.php
@@ -11,8 +11,6 @@
namespace Symfony\Component\Form;
-use Symfony\Component\Form\Exception\UnexpectedTypeException;
-
class FormFactory implements FormFactoryInterface
{
private $registry;
@@ -25,7 +23,7 @@ public function __construct(FormRegistryInterface $registry)
/**
* {@inheritdoc}
*/
- public function create($type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
+ public function create(string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
return $this->createBuilder($type, $data, $options)->getForm();
}
@@ -33,7 +31,7 @@ public function create($type = 'Symfony\Component\Form\Extension\Core\Type\FormT
/**
* {@inheritdoc}
*/
- public function createNamed($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
+ public function createNamed(string $name, string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
return $this->createNamedBuilder($name, $type, $data, $options)->getForm();
}
@@ -41,7 +39,7 @@ public function createNamed($name, $type = 'Symfony\Component\Form\Extension\Cor
/**
* {@inheritdoc}
*/
- public function createForProperty($class, $property, $data = null, array $options = [])
+ public function createForProperty(string $class, string $property, $data = null, array $options = [])
{
return $this->createBuilderForProperty($class, $property, $data, $options)->getForm();
}
@@ -49,28 +47,20 @@ public function createForProperty($class, $property, $data = null, array $option
/**
* {@inheritdoc}
*/
- public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
+ public function createBuilder(string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
- if (!\is_string($type)) {
- throw new UnexpectedTypeException($type, 'string');
- }
-
return $this->createNamedBuilder($this->registry->getType($type)->getBlockPrefix(), $type, $data, $options);
}
/**
* {@inheritdoc}
*/
- public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
+ public function createNamedBuilder(string $name, string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
if (null !== $data && !\array_key_exists('data', $options)) {
$options['data'] = $data;
}
- if (!\is_string($type)) {
- throw new UnexpectedTypeException($type, 'string');
- }
-
$type = $this->registry->getType($type);
$builder = $type->createBuilder($this, (string) $name, $options);
@@ -85,7 +75,7 @@ public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extens
/**
* {@inheritdoc}
*/
- public function createBuilderForProperty($class, $property, $data = null, array $options = [])
+ public function createBuilderForProperty(string $class, string $property, $data = null, array $options = [])
{
if (null === $guesser = $this->registry->getTypeGuesser()) {
return $this->createNamedBuilder($property, 'Symfony\Component\Form\Extension\Core\Type\TextType', $data, $options);
diff --git a/src/Symfony/Component/Form/FormFactoryBuilder.php b/src/Symfony/Component/Form/FormFactoryBuilder.php
index f7644bb1f6abb..94c2367ec60de 100644
--- a/src/Symfony/Component/Form/FormFactoryBuilder.php
+++ b/src/Symfony/Component/Form/FormFactoryBuilder.php
@@ -109,12 +109,8 @@ public function addTypes(array $types)
*/
public function addTypeExtension(FormTypeExtensionInterface $typeExtension)
{
- if (method_exists($typeExtension, 'getExtendedTypes')) {
- foreach ($typeExtension::getExtendedTypes() as $extendedType) {
- $this->typeExtensions[$extendedType][] = $typeExtension;
- }
- } else {
- $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension;
+ foreach ($typeExtension::getExtendedTypes() as $extendedType) {
+ $this->typeExtensions[$extendedType][] = $typeExtension;
}
return $this;
diff --git a/src/Symfony/Component/Form/FormFactoryInterface.php b/src/Symfony/Component/Form/FormFactoryInterface.php
index 5719962724c36..2f1acf5424e4c 100644
--- a/src/Symfony/Component/Form/FormFactoryInterface.php
+++ b/src/Symfony/Component/Form/FormFactoryInterface.php
@@ -23,29 +23,26 @@ interface FormFactoryInterface
*
* @see createBuilder()
*
- * @param string $type The type of the form
- * @param mixed $data The initial data
+ * @param mixed $data The initial data
*
* @return FormInterface The form named after the type
*
* @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type
*/
- public function create($type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
+ public function create(string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
/**
* Returns a form.
*
* @see createNamedBuilder()
*
- * @param string $name The name of the form
- * @param string $type The type of the form
- * @param mixed $data The initial data
+ * @param mixed $data The initial data
*
* @return FormInterface The form
*
* @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type
*/
- public function createNamed($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
+ public function createNamed(string $name, string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
/**
* Returns a form for a property of a class.
@@ -60,32 +57,29 @@ public function createNamed($name, $type = 'Symfony\Component\Form\Extension\Cor
*
* @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the form type
*/
- public function createForProperty($class, $property, $data = null, array $options = []);
+ public function createForProperty(string $class, string $property, $data = null, array $options = []);
/**
* Returns a form builder.
*
- * @param string $type The type of the form
- * @param mixed $data The initial data
+ * @param mixed $data The initial data
*
* @return FormBuilderInterface The form builder
*
* @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type
*/
- public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
+ public function createBuilder(string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
/**
* Returns a form builder.
*
- * @param string $name The name of the form
- * @param string $type The type of the form
- * @param mixed $data The initial data
+ * @param mixed $data The initial data
*
* @return FormBuilderInterface The form builder
*
* @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type
*/
- public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
+ public function createNamedBuilder(string $name, string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = []);
/**
* Returns a form builder for a property of a class.
@@ -101,5 +95,5 @@ public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extens
*
* @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the form type
*/
- public function createBuilderForProperty($class, $property, $data = null, array $options = []);
+ public function createBuilderForProperty(string $class, string $property, $data = null, array $options = []);
}
diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php
index 9be500818274e..ba3d644cd95d5 100644
--- a/src/Symfony/Component/Form/FormInterface.php
+++ b/src/Symfony/Component/Form/FormInterface.php
@@ -53,38 +53,32 @@ public function getParent();
* @throws Exception\LogicException when trying to add a child to a non-compound form
* @throws Exception\UnexpectedTypeException if $child or $type has an unexpected type
*/
- public function add($child, $type = null, array $options = []);
+ public function add($child, string $type = null, array $options = []);
/**
* Returns the child with the given name.
*
- * @param string $name The name of the child
- *
* @return self
*
* @throws \OutOfBoundsException if the named child does not exist
*/
- public function get($name);
+ public function get(string $name);
/**
* Returns whether a child with the given name exists.
*
- * @param string $name The name of the child
- *
* @return bool
*/
- public function has($name);
+ public function has(string $name);
/**
* Removes a child from the form.
*
- * @param string $name The name of the child to remove
- *
* @return $this
*
* @throws Exception\AlreadySubmittedException if the form has already been submitted
*/
- public function remove($name);
+ public function remove(string $name);
/**
* Returns all children in this group.
@@ -103,7 +97,7 @@ public function all();
* @return FormErrorIterator An iterator over the {@link FormError}
* instances that where added to this form
*/
- public function getErrors($deep = false, $flatten = true);
+ public function getErrors(bool $deep = false, bool $flatten = true);
/**
* Updates the form with default model data.
@@ -309,7 +303,7 @@ public function handleRequest($request = null);
*
* @throws Exception\AlreadySubmittedException if the form has already been submitted
*/
- public function submit($submittedData, $clearMissing = true);
+ public function submit($submittedData, bool $clearMissing = true);
/**
* Returns the root of the form tree.
diff --git a/src/Symfony/Component/Form/FormRegistry.php b/src/Symfony/Component/Form/FormRegistry.php
index 8583054c20683..d7ae85b2aff41 100644
--- a/src/Symfony/Component/Form/FormRegistry.php
+++ b/src/Symfony/Component/Form/FormRegistry.php
@@ -67,7 +67,7 @@ public function __construct(array $extensions, ResolvedFormTypeFactoryInterface
/**
* {@inheritdoc}
*/
- public function getType($name)
+ public function getType(string $name)
{
if (!isset($this->types[$name])) {
$type = null;
@@ -134,7 +134,7 @@ private function resolveType(FormTypeInterface $type): ResolvedFormTypeInterface
/**
* {@inheritdoc}
*/
- public function hasType($name)
+ public function hasType(string $name)
{
if (isset($this->types[$name])) {
return true;
diff --git a/src/Symfony/Component/Form/FormRegistryInterface.php b/src/Symfony/Component/Form/FormRegistryInterface.php
index f16c0cb8fbb16..fd8ed0b172958 100644
--- a/src/Symfony/Component/Form/FormRegistryInterface.php
+++ b/src/Symfony/Component/Form/FormRegistryInterface.php
@@ -23,22 +23,18 @@ interface FormRegistryInterface
*
* This methods registers the type extensions from the form extensions.
*
- * @param string $name The name of the type
- *
* @return ResolvedFormTypeInterface The type
*
* @throws Exception\InvalidArgumentException if the type can not be retrieved from any extension
*/
- public function getType($name);
+ public function getType(string $name);
/**
* Returns whether the given form type is supported.
*
- * @param string $name The name of the type
- *
* @return bool Whether the type is supported
*/
- public function hasType($name);
+ public function hasType(string $name);
/**
* Returns the guesser responsible for guessing types.
diff --git a/src/Symfony/Component/Form/FormRenderer.php b/src/Symfony/Component/Form/FormRenderer.php
index 034dc41cc3b47..c656283794207 100644
--- a/src/Symfony/Component/Form/FormRenderer.php
+++ b/src/Symfony/Component/Form/FormRenderer.php
@@ -48,7 +48,7 @@ public function getEngine()
/**
* {@inheritdoc}
*/
- public function setTheme(FormView $view, $themes, $useDefaultThemes = true)
+ public function setTheme(FormView $view, $themes, bool $useDefaultThemes = true)
{
$this->engine->setTheme($view, $themes, $useDefaultThemes);
}
@@ -56,7 +56,7 @@ public function setTheme(FormView $view, $themes, $useDefaultThemes = true)
/**
* {@inheritdoc}
*/
- public function renderCsrfToken($tokenId)
+ public function renderCsrfToken(string $tokenId)
{
if (null === $this->csrfTokenManager) {
throw new BadMethodCallException('CSRF tokens can only be generated if a CsrfTokenManagerInterface is injected in FormRenderer::__construct().');
@@ -68,7 +68,7 @@ public function renderCsrfToken($tokenId)
/**
* {@inheritdoc}
*/
- public function renderBlock(FormView $view, $blockName, array $variables = [])
+ public function renderBlock(FormView $view, string $blockName, array $variables = [])
{
$resource = $this->engine->getResourceForBlockName($view, $blockName);
@@ -127,15 +127,13 @@ public function renderBlock(FormView $view, $blockName, array $variables = [])
/**
* {@inheritdoc}
*/
- public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = [])
+ public function searchAndRenderBlock(FormView $view, string $blockNameSuffix, array $variables = [])
{
$renderOnlyOnce = 'row' === $blockNameSuffix || 'widget' === $blockNameSuffix;
if ($renderOnlyOnce && $view->isRendered()) {
// This is not allowed, because it would result in rendering same IDs multiple times, which is not valid.
- @trigger_error(sprintf('You are calling "form_%s" for field "%s" which has already been rendered before, trying to render fields which were already rendered is deprecated since Symfony 4.2 and will throw an exception in 5.0.', $blockNameSuffix, $view->vars['name']), E_USER_DEPRECATED);
- // throw new BadMethodCallException(sprintf('Field "%s" has already been rendered. Save result of previous render call to variable and output that instead.', $view->vars['name']));
- return '';
+ throw new BadMethodCallException(sprintf('Field "%s" has already been rendered, save the result of previous render call to a variable and output that instead.', $view->vars['name']));
}
// The cache key for storing the variables and types
@@ -282,7 +280,7 @@ public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $va
/**
* {@inheritdoc}
*/
- public function humanize($text)
+ public function humanize(string $text)
{
return ucfirst(strtolower(trim(preg_replace(['/([A-Z])/', '/[_\s]+/'], ['_$1', ' '], $text))));
}
diff --git a/src/Symfony/Component/Form/FormRendererEngineInterface.php b/src/Symfony/Component/Form/FormRendererEngineInterface.php
index 4743ffea3f307..2dd2a2fc59f20 100644
--- a/src/Symfony/Component/Form/FormRendererEngineInterface.php
+++ b/src/Symfony/Component/Form/FormRendererEngineInterface.php
@@ -21,13 +21,11 @@ interface FormRendererEngineInterface
/**
* Sets the theme(s) to be used for rendering a view and its children.
*
- * @param FormView $view The view to assign the theme(s) to
- * @param mixed $themes The theme(s). The type of these themes
- * is open to the implementation.
- * @param bool $useDefaultThemes If true, will use default themes specified
- * in the engine
+ * @param FormView $view The view to assign the theme(s) to
+ * @param mixed $themes The theme(s). The type of these themes
+ * is open to the implementation.
*/
- public function setTheme(FormView $view, $themes, $useDefaultThemes = true);
+ public function setTheme(FormView $view, $themes, bool $useDefaultThemes = true);
/**
* Returns the resource for a block name.
@@ -38,15 +36,14 @@ public function setTheme(FormView $view, $themes, $useDefaultThemes = true);
* The type of the resource is decided by the implementation. The resource
* is later passed to {@link renderBlock()} by the rendering algorithm.
*
- * @param FormView $view The view for determining the used themes.
- * First the themes attached directly to the
- * view with {@link setTheme()} are considered,
- * then the ones of its parent etc.
- * @param string $blockName The name of the block to render
+ * @param FormView $view The view for determining the used themes.
+ * First the themes attached directly to the
+ * view with {@link setTheme()} are considered,
+ * then the ones of its parent etc.
*
* @return mixed the renderer resource or false, if none was found
*/
- public function getResourceForBlockName(FormView $view, $blockName);
+ public function getResourceForBlockName(FormView $view, string $blockName);
/**
* Returns the resource for a block hierarchy.
@@ -82,7 +79,7 @@ public function getResourceForBlockName(FormView $view, $blockName);
*
* @return mixed The renderer resource or false, if none was found
*/
- public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, $hierarchyLevel);
+ public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, int $hierarchyLevel);
/**
* Returns the hierarchy level at which a resource can be found.
@@ -120,7 +117,7 @@ public function getResourceForBlockNameHierarchy(FormView $view, array $blockNam
*
* @return int|bool The hierarchy level or false, if no resource was found
*/
- public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, $hierarchyLevel);
+ public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, int $hierarchyLevel);
/**
* Renders a block in the given renderer resource.
@@ -131,10 +128,9 @@ public function getResourceHierarchyLevel(FormView $view, array $blockNameHierar
*
* @param FormView $view The view to render
* @param mixed $resource The renderer resource
- * @param string $blockName The name of the block to render
* @param array $variables The variables to pass to the template
*
* @return string The HTML markup
*/
- public function renderBlock(FormView $view, $resource, $blockName, array $variables = []);
+ public function renderBlock(FormView $view, $resource, string $blockName, array $variables = []);
}
diff --git a/src/Symfony/Component/Form/FormRendererInterface.php b/src/Symfony/Component/Form/FormRendererInterface.php
index 930f61d583bf5..16c29744edd97 100644
--- a/src/Symfony/Component/Form/FormRendererInterface.php
+++ b/src/Symfony/Component/Form/FormRendererInterface.php
@@ -34,18 +34,17 @@ public function getEngine();
* @param bool $useDefaultThemes If true, will use default themes specified
* in the renderer
*/
- public function setTheme(FormView $view, $themes, $useDefaultThemes = true);
+ public function setTheme(FormView $view, $themes, bool $useDefaultThemes = true);
/**
* Renders a named block of the form theme.
*
* @param FormView $view The view for which to render the block
- * @param string $blockName The name of the block
* @param array $variables The variables to pass to the template
*
* @return string The HTML markup
*/
- public function renderBlock(FormView $view, $blockName, array $variables = []);
+ public function renderBlock(FormView $view, string $blockName, array $variables = []);
/**
* Searches and renders a block for a given name suffix.
@@ -57,13 +56,12 @@ public function renderBlock(FormView $view, $blockName, array $variables = []);
* If this method is called recursively, the block search is continued
* where a block was found before.
*
- * @param FormView $view The view for which to render the block
- * @param string $blockNameSuffix The suffix of the block name
- * @param array $variables The variables to pass to the template
+ * @param FormView $view The view for which to render the block
+ * @param array $variables The variables to pass to the template
*
* @return string The HTML markup
*/
- public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = []);
+ public function searchAndRenderBlock(FormView $view, string $blockNameSuffix, array $variables = []);
/**
* Renders a CSRF token.
@@ -80,11 +78,9 @@ public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $va
* throw new \RuntimeException('CSRF attack detected.');
* }
*
- * @param string $tokenId The ID of the CSRF token
- *
* @return string A CSRF token
*/
- public function renderCsrfToken($tokenId);
+ public function renderCsrfToken(string $tokenId);
/**
* Makes a technical name human readable.
@@ -93,9 +89,7 @@ public function renderCsrfToken($tokenId);
* of the resulting string is capitalized, while all other letters are
* turned to lowercase.
*
- * @param string $text The text to humanize
- *
* @return string The humanized text
*/
- public function humanize($text);
+ public function humanize(string $text);
}
diff --git a/src/Symfony/Component/Form/FormTypeExtensionInterface.php b/src/Symfony/Component/Form/FormTypeExtensionInterface.php
index 109cd7183b9e5..6810f0ae91e12 100644
--- a/src/Symfony/Component/Form/FormTypeExtensionInterface.php
+++ b/src/Symfony/Component/Form/FormTypeExtensionInterface.php
@@ -15,8 +15,6 @@
/**
* @author Bernhard Schussek
- *
- * @method static iterable getExtendedTypes() Gets the extended types - not implementing it is deprecated since Symfony 4.2
*/
interface FormTypeExtensionInterface
{
@@ -53,11 +51,9 @@ public function finishView(FormView $view, FormInterface $form, array $options);
public function configureOptions(OptionsResolver $resolver);
/**
- * Returns the name of the type being extended.
- *
- * @return string The name of the type being extended
+ * Gets the extended types.
*
- * @deprecated since Symfony 4.2, use getExtendedTypes() instead.
+ * @return string[]
*/
- public function getExtendedType();
+ public static function getExtendedTypes(): iterable;
}
diff --git a/src/Symfony/Component/Form/FormTypeGuesserChain.php b/src/Symfony/Component/Form/FormTypeGuesserChain.php
index 0a3450fc33d0f..e91d2708577ff 100644
--- a/src/Symfony/Component/Form/FormTypeGuesserChain.php
+++ b/src/Symfony/Component/Form/FormTypeGuesserChain.php
@@ -41,7 +41,7 @@ public function __construct(iterable $guessers)
/**
* {@inheritdoc}
*/
- public function guessType($class, $property)
+ public function guessType(string $class, string $property)
{
return $this->guess(function ($guesser) use ($class, $property) {
return $guesser->guessType($class, $property);
@@ -51,7 +51,7 @@ public function guessType($class, $property)
/**
* {@inheritdoc}
*/
- public function guessRequired($class, $property)
+ public function guessRequired(string $class, string $property)
{
return $this->guess(function ($guesser) use ($class, $property) {
return $guesser->guessRequired($class, $property);
@@ -61,7 +61,7 @@ public function guessRequired($class, $property)
/**
* {@inheritdoc}
*/
- public function guessMaxLength($class, $property)
+ public function guessMaxLength(string $class, string $property)
{
return $this->guess(function ($guesser) use ($class, $property) {
return $guesser->guessMaxLength($class, $property);
@@ -71,7 +71,7 @@ public function guessMaxLength($class, $property)
/**
* {@inheritdoc}
*/
- public function guessPattern($class, $property)
+ public function guessPattern(string $class, string $property)
{
return $this->guess(function ($guesser) use ($class, $property) {
return $guesser->guessPattern($class, $property);
diff --git a/src/Symfony/Component/Form/FormTypeGuesserInterface.php b/src/Symfony/Component/Form/FormTypeGuesserInterface.php
index 3be9a0c9f8570..e172df4911061 100644
--- a/src/Symfony/Component/Form/FormTypeGuesserInterface.php
+++ b/src/Symfony/Component/Form/FormTypeGuesserInterface.php
@@ -19,32 +19,23 @@ interface FormTypeGuesserInterface
/**
* Returns a field guess for a property name of a class.
*
- * @param string $class The fully qualified class name
- * @param string $property The name of the property to guess for
- *
* @return Guess\TypeGuess|null A guess for the field's type and options
*/
- public function guessType($class, $property);
+ public function guessType(string $class, string $property);
/**
* Returns a guess whether a property of a class is required.
*
- * @param string $class The fully qualified class name
- * @param string $property The name of the property to guess for
- *
* @return Guess\ValueGuess|null A guess for the field's required setting
*/
- public function guessRequired($class, $property);
+ public function guessRequired(string $class, string $property);
/**
* Returns a guess about the field's maximum length.
*
- * @param string $class The fully qualified class name
- * @param string $property The name of the property to guess for
- *
* @return Guess\ValueGuess|null A guess for the field's maximum length
*/
- public function guessMaxLength($class, $property);
+ public function guessMaxLength(string $class, string $property);
/**
* Returns a guess about the field's pattern.
@@ -56,10 +47,7 @@ public function guessMaxLength($class, $property);
*
* @see https://github.com/symfony/symfony/pull/3927
*
- * @param string $class The fully qualified class name
- * @param string $property The name of the property to guess for
- *
* @return Guess\ValueGuess|null A guess for the field's required pattern
*/
- public function guessPattern($class, $property);
+ public function guessPattern(string $class, string $property);
}
diff --git a/src/Symfony/Component/Form/PreloadedExtension.php b/src/Symfony/Component/Form/PreloadedExtension.php
index 2f1cc2377f8e0..3970c014c23f7 100644
--- a/src/Symfony/Component/Form/PreloadedExtension.php
+++ b/src/Symfony/Component/Form/PreloadedExtension.php
@@ -32,14 +32,6 @@ class PreloadedExtension implements FormExtensionInterface
*/
public function __construct(array $types, array $typeExtensions, FormTypeGuesserInterface $typeGuesser = null)
{
- foreach ($typeExtensions as $extensions) {
- foreach ($extensions as $typeExtension) {
- if (!method_exists($typeExtension, 'getExtendedTypes')) {
- @trigger_error(sprintf('Not implementing the "%s::getExtendedTypes()" method in "%s" is deprecated since Symfony 4.2.', FormTypeExtensionInterface::class, \get_class($typeExtension)), E_USER_DEPRECATED);
- }
- }
- }
-
$this->typeExtensions = $typeExtensions;
$this->typeGuesser = $typeGuesser;
@@ -51,7 +43,7 @@ public function __construct(array $types, array $typeExtensions, FormTypeGuesser
/**
* {@inheritdoc}
*/
- public function getType($name)
+ public function getType(string $name)
{
if (!isset($this->types[$name])) {
throw new InvalidArgumentException(sprintf('The type "%s" can not be loaded by this extension', $name));
@@ -63,7 +55,7 @@ public function getType($name)
/**
* {@inheritdoc}
*/
- public function hasType($name)
+ public function hasType(string $name)
{
return isset($this->types[$name]);
}
@@ -71,7 +63,7 @@ public function hasType($name)
/**
* {@inheritdoc}
*/
- public function getTypeExtensions($name)
+ public function getTypeExtensions(string $name)
{
return isset($this->typeExtensions[$name])
? $this->typeExtensions[$name]
@@ -81,7 +73,7 @@ public function getTypeExtensions($name)
/**
* {@inheritdoc}
*/
- public function hasTypeExtensions($name)
+ public function hasTypeExtensions(string $name)
{
return !empty($this->typeExtensions[$name]);
}
diff --git a/src/Symfony/Component/Form/ResolvedFormType.php b/src/Symfony/Component/Form/ResolvedFormType.php
index 6b3fb6837edc6..7815d4f833fe3 100644
--- a/src/Symfony/Component/Form/ResolvedFormType.php
+++ b/src/Symfony/Component/Form/ResolvedFormType.php
@@ -91,7 +91,7 @@ public function getTypeExtensions()
/**
* {@inheritdoc}
*/
- public function createBuilder(FormFactoryInterface $factory, $name, array $options = [])
+ public function createBuilder(FormFactoryInterface $factory, string $name, array $options = [])
{
try {
$options = $this->getOptionsResolver()->resolve($options);
@@ -198,12 +198,9 @@ public function getOptionsResolver()
*
* Override this method if you want to customize the builder class.
*
- * @param string $name The name of the builder
- * @param string|null $dataClass The data class
- *
* @return FormBuilderInterface The new builder instance
*/
- protected function newBuilder($name, $dataClass, FormFactoryInterface $factory, array $options)
+ protected function newBuilder(string $name, ?string $dataClass, FormFactoryInterface $factory, array $options)
{
if ($this->innerType instanceof ButtonTypeInterface) {
return new ButtonBuilder($name, $options);
diff --git a/src/Symfony/Component/Form/ResolvedFormTypeInterface.php b/src/Symfony/Component/Form/ResolvedFormTypeInterface.php
index e38d160285ab2..8f01254cb4e94 100644
--- a/src/Symfony/Component/Form/ResolvedFormTypeInterface.php
+++ b/src/Symfony/Component/Form/ResolvedFormTypeInterface.php
@@ -55,7 +55,7 @@ public function getTypeExtensions();
*
* @return FormBuilderInterface The created form builder
*/
- public function createBuilder(FormFactoryInterface $factory, $name, array $options = []);
+ public function createBuilder(FormFactoryInterface $factory, string $name, array $options = []);
/**
* Creates a new form view for a form of this type.
diff --git a/src/Symfony/Component/Form/SubmitButton.php b/src/Symfony/Component/Form/SubmitButton.php
index a838542f975d8..5e38b10987282 100644
--- a/src/Symfony/Component/Form/SubmitButton.php
+++ b/src/Symfony/Component/Form/SubmitButton.php
@@ -18,9 +18,6 @@
*/
class SubmitButton extends Button implements ClickableInterface
{
- /**
- * @var bool
- */
private $clicked = false;
/**
@@ -41,7 +38,7 @@ public function isClicked()
*
* @throws Exception\AlreadySubmittedException if the form has already been submitted
*/
- public function submit($submittedData, $clearMissing = true)
+ public function submit($submittedData, bool $clearMissing = true)
{
if ($this->getConfig()->getDisabled()) {
$this->clicked = false;
diff --git a/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php b/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php
index eabf82d161cf4..4feb8df5a6567 100644
--- a/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php
+++ b/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php
@@ -20,14 +20,12 @@
*/
abstract class FormIntegrationTestCase extends TestCase
{
- use ForwardCompatTestTrait;
-
/**
* @var FormFactoryInterface
*/
protected $factory;
- private function doSetUp()
+ protected function setUp(): void
{
$this->factory = Forms::createFormFactoryBuilder()
->addExtensions($this->getExtensions())
diff --git a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
index e11ce03357f6d..732f9ec3dd02b 100644
--- a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
+++ b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
@@ -45,17 +45,15 @@ protected function runTest()
}
/**
- * @param int $maxRunningTime
- *
* @throws \InvalidArgumentException
*/
- public function setMaxRunningTime($maxRunningTime)
+ public function setMaxRunningTime(int $maxRunningTime)
{
- if (\is_int($maxRunningTime) && $maxRunningTime >= 0) {
- $this->maxRunningTime = $maxRunningTime;
- } else {
+ if ($maxRunningTime < 0) {
throw new \InvalidArgumentException();
}
+
+ $this->maxRunningTime = $maxRunningTime;
}
/**
diff --git a/src/Symfony/Component/Form/Test/ForwardCompatTestTrait.php b/src/Symfony/Component/Form/Test/ForwardCompatTestTrait.php
deleted file mode 100644
index 82e531d0302a9..0000000000000
--- a/src/Symfony/Component/Form/Test/ForwardCompatTestTrait.php
+++ /dev/null
@@ -1,78 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Test;
-
-use PHPUnit\Framework\TestCase;
-
-// Auto-adapt to PHPUnit 8 that added a `void` return-type to the setUp/tearDown methods
-
-if ((new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) {
- /**
- * @internal
- */
- trait ForwardCompatTestTrait
- {
- private function doSetUp(): void
- {
- }
-
- private function doTearDown(): void
- {
- }
-
- protected function setUp(): void
- {
- $this->doSetUp();
- }
-
- protected function tearDown(): void
- {
- $this->doTearDown();
- }
- }
-} else {
- /**
- * @internal
- */
- trait ForwardCompatTestTrait
- {
- /**
- * @return void
- */
- private function doSetUp()
- {
- }
-
- /**
- * @return void
- */
- private function doTearDown()
- {
- }
-
- /**
- * @return void
- */
- protected function setUp()
- {
- $this->doSetUp();
- }
-
- /**
- * @return void
- */
- protected function tearDown()
- {
- $this->doTearDown();
- }
- }
-}
diff --git a/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php b/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php
index 46bd8b7e576d4..254883f871e56 100644
--- a/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php
+++ b/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php
@@ -24,10 +24,7 @@ trait ValidatorExtensionTrait
*/
protected $validator;
- /**
- * @return ValidatorExtension
- */
- protected function getValidatorExtension()
+ protected function getValidatorExtension(): ValidatorExtension
{
if (!interface_exists(ValidatorInterface::class)) {
throw new \Exception('In order to use the "ValidatorExtensionTrait", the symfony/validator component must be installed');
diff --git a/src/Symfony/Component/Form/Test/TypeTestCase.php b/src/Symfony/Component/Form/Test/TypeTestCase.php
index 51e6b85c6eca5..12d24eba73a28 100644
--- a/src/Symfony/Component/Form/Test/TypeTestCase.php
+++ b/src/Symfony/Component/Form/Test/TypeTestCase.php
@@ -17,8 +17,6 @@
abstract class TypeTestCase extends FormIntegrationTestCase
{
- use ForwardCompatTestTrait;
-
/**
* @var FormBuilder
*/
@@ -29,7 +27,7 @@ abstract class TypeTestCase extends FormIntegrationTestCase
*/
protected $dispatcher;
- private function doSetUp()
+ protected function setUp(): void
{
parent::setUp();
@@ -37,7 +35,7 @@ private function doSetUp()
$this->builder = new FormBuilder('', null, $this->dispatcher, $this->factory);
}
- private function doTearDown()
+ protected function tearDown(): void
{
if (\in_array(ValidatorExtensionTrait::class, class_uses($this))) {
$this->validator = null;
diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
index e860557468024..9c5594bcb8dd6 100644
--- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
@@ -1521,29 +1521,6 @@ public function testDateTimeWithWidgetSingleText()
);
}
- /**
- * @group legacy
- */
- public function testDateTimeWithWidgetSingleTextIgnoreDateAndTimeWidgets()
- {
- $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', [
- 'input' => 'string',
- 'date_widget' => 'choice',
- 'time_widget' => 'choice',
- 'widget' => 'single_text',
- 'model_timezone' => 'UTC',
- 'view_timezone' => 'UTC',
- ]);
-
- $this->assertWidgetMatchesXpath($form->createView(), [],
-'/input
- [@type="datetime-local"]
- [@name="name"]
- [@value="2011-02-03T04:05:06"]
-'
- );
- }
-
public function testDateChoice()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', date('Y').'-02-03', [
diff --git a/src/Symfony/Component/Form/Tests/AbstractTypeExtensionTest.php b/src/Symfony/Component/Form/Tests/AbstractTypeExtensionTest.php
deleted file mode 100644
index b50812ffa1785..0000000000000
--- a/src/Symfony/Component/Form/Tests/AbstractTypeExtensionTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
-
-class AbstractTypeExtensionTest extends TestCase
-{
- /**
- * @group legacy
- */
- public function testImplementingNeitherGetExtendedTypeNorExtendsTypeThrowsException()
- {
- $this->expectException('Symfony\Component\Form\Exception\LogicException');
- $this->expectExceptionMessage('You need to implement the static getExtendedTypes() method when implementing the Symfony\Component\Form\FormTypeExtensionInterface in Symfony\Component\Form\Tests\TypeExtensionWithoutExtendedTypes.');
- $extension = new TypeExtensionWithoutExtendedTypes();
- $extension->getExtendedType();
- }
-
- /**
- * @group legacy
- * @expectedDeprecation The Symfony\Component\Form\Tests\MultipleTypesExtension::getExtendedType() method is deprecated since Symfony 4.2 and will be removed in 5.0. Use getExtendedTypes() instead.
- */
- public function testGetExtendedTypeReturnsFirstConfiguredExtension()
- {
- $extension = new MultipleTypesExtension();
-
- $this->assertSame(DateTimeType::class, $extension->getExtendedType());
- }
-}
diff --git a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php
index 7bd78896d1947..b5b4a38172175 100644
--- a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php
+++ b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php
@@ -39,11 +39,11 @@ public function testValidNames($name)
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder($name));
}
- /**
- * @group legacy
- */
public function testNameContainingIllegalCharacters()
{
+ $this->expectException('Symfony\Component\Form\Exception\InvalidArgumentException');
+ $this->expectExceptionMessage('The name "button[]" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").');
+
$this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder('button[]'));
}
diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php
index 39d54c536a513..55e01dd206c1d 100644
--- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php
+++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php
@@ -14,8 +14,12 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
+use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
+use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
+use Symfony\Component\Form\FormTypeInterface;
/**
* @author Bernhard Schussek
@@ -134,7 +138,7 @@ public function testCreateFromChoicesSameValueClosure()
$list = new ArrayChoiceList([]);
$closure = function () {};
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->exactly(2))
->method('createListFromChoices')
->with($choices, $closure)
->willReturn($list);
@@ -143,6 +147,23 @@ public function testCreateFromChoicesSameValueClosure()
$this->assertSame($list, $this->factory->createListFromChoices($choices, $closure));
}
+ public function testCreateFromChoicesSameValueClosureUseCache()
+ {
+ $choices = [1];
+ $list = new ArrayChoiceList([]);
+ $formType = $this->createMock(FormTypeInterface::class);
+ $valueCallback = function () {};
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createListFromChoices')
+ ->with($choices, $valueCallback)
+ ->willReturn($list)
+ ;
+
+ $this->assertSame($list, $this->factory->createListFromChoices($choices, ChoiceList::value($formType, $valueCallback)));
+ $this->assertSame($list, $this->factory->createListFromChoices($choices, ChoiceList::value($formType, function () {})));
+ }
+
public function testCreateFromChoicesDifferentValueClosure()
{
$choices = [1];
@@ -168,14 +189,37 @@ public function testCreateFromLoaderSameLoader()
{
$loader = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface')->getMock();
$list = new ArrayChoiceList([]);
+ $list2 = new ArrayChoiceList([]);
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createListFromLoader')
->with($loader)
- ->willReturn($list);
+ ->willReturn($list)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createListFromLoader')
+ ->with($loader)
+ ->willReturn($list2)
+ ;
$this->assertSame($list, $this->factory->createListFromLoader($loader));
- $this->assertSame($list, $this->factory->createListFromLoader($loader));
+ $this->assertSame($list2, $this->factory->createListFromLoader($loader));
+ }
+
+ public function testCreateFromLoaderSameLoaderUseCache()
+ {
+ $type = $this->createMock(FormTypeInterface::class);
+ $loader = $this->createMock(ChoiceLoaderInterface::class);
+ $list = new ArrayChoiceList([]);
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createListFromLoader')
+ ->with($loader)
+ ->willReturn($list)
+ ;
+
+ $this->assertSame($list, $this->factory->createListFromLoader(ChoiceList::loader($type, $loader)));
+ $this->assertSame($list, $this->factory->createListFromLoader(ChoiceList::loader($type, $this->createMock(ChoiceLoaderInterface::class))));
}
public function testCreateFromLoaderDifferentLoader()
@@ -201,21 +245,53 @@ public function testCreateFromLoaderDifferentLoader()
public function testCreateFromLoaderSameValueClosure()
{
$loader = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface')->getMock();
+ $type = $this->createMock(FormTypeInterface::class);
$list = new ArrayChoiceList([]);
+ $list2 = new ArrayChoiceList([]);
$closure = function () {};
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createListFromLoader')
->with($loader, $closure)
- ->willReturn($list);
+ ->willReturn($list)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createListFromLoader')
+ ->with($loader, $closure)
+ ->willReturn($list2)
+ ;
+
+ $this->assertSame($list, $this->factory->createListFromLoader(ChoiceList::loader($type, $loader), $closure));
+ $this->assertSame($list2, $this->factory->createListFromLoader(ChoiceList::loader($type, $this->createMock(ChoiceLoaderInterface::class)), $closure));
+ }
+
+ public function testCreateFromLoaderSameValueClosureUseCache()
+ {
+ $type = $this->createMock(FormTypeInterface::class);
+ $loader = $this->createMock(ChoiceLoaderInterface::class);
+ $list = new ArrayChoiceList([]);
+ $closure = function () {};
- $this->assertSame($list, $this->factory->createListFromLoader($loader, $closure));
- $this->assertSame($list, $this->factory->createListFromLoader($loader, $closure));
+ $this->decoratedFactory->expects($this->once())
+ ->method('createListFromLoader')
+ ->with($loader, $closure)
+ ->willReturn($list)
+ ;
+
+ $this->assertSame($list, $this->factory->createListFromLoader(
+ ChoiceList::loader($type, $loader),
+ ChoiceList::value($type, $closure)
+ ));
+ $this->assertSame($list, $this->factory->createListFromLoader(
+ ChoiceList::loader($type, $this->createMock(ChoiceLoaderInterface::class)),
+ ChoiceList::value($type, function () {})
+ ));
}
public function testCreateFromLoaderDifferentValueClosure()
{
$loader = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface')->getMock();
+ $type = $this->createMock(FormTypeInterface::class);
$list1 = new ArrayChoiceList([]);
$list2 = new ArrayChoiceList([]);
$closure1 = function () {};
@@ -230,8 +306,8 @@ public function testCreateFromLoaderDifferentValueClosure()
->with($loader, $closure2)
->willReturn($list2);
- $this->assertSame($list1, $this->factory->createListFromLoader($loader, $closure1));
- $this->assertSame($list2, $this->factory->createListFromLoader($loader, $closure2));
+ $this->assertSame($list1, $this->factory->createListFromLoader(ChoiceList::loader($type, $loader), $closure1));
+ $this->assertSame($list2, $this->factory->createListFromLoader(ChoiceList::loader($type, $this->createMock(ChoiceLoaderInterface::class)), $closure2));
}
public function testCreateViewSamePreferredChoices()
@@ -239,14 +315,38 @@ public function testCreateViewSamePreferredChoices()
$preferred = ['a'];
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createView')
->with($list, $preferred)
- ->willReturn($view);
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, $preferred)
+ ->willReturn($view2)
+ ;
$this->assertSame($view, $this->factory->createView($list, $preferred));
- $this->assertSame($view, $this->factory->createView($list, $preferred));
+ $this->assertSame($view2, $this->factory->createView($list, $preferred));
+ }
+
+ public function testCreateViewSamePreferredChoicesUseCache()
+ {
+ $preferred = ['a'];
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createView')
+ ->with($list, $preferred)
+ ->willReturn($view)
+ ;
+
+ $this->assertSame($view, $this->factory->createView($list, ChoiceList::preferred($type, $preferred)));
+ $this->assertSame($view, $this->factory->createView($list, ChoiceList::preferred($type, ['a'])));
}
public function testCreateViewDifferentPreferredChoices()
@@ -275,14 +375,38 @@ public function testCreateViewSamePreferredChoicesClosure()
$preferred = function () {};
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createView')
->with($list, $preferred)
- ->willReturn($view);
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, $preferred)
+ ->willReturn($view2)
+ ;
$this->assertSame($view, $this->factory->createView($list, $preferred));
- $this->assertSame($view, $this->factory->createView($list, $preferred));
+ $this->assertSame($view2, $this->factory->createView($list, $preferred));
+ }
+
+ public function testCreateViewSamePreferredChoicesClosureUseCache()
+ {
+ $preferredCallback = function () {};
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createView')
+ ->with($list, $preferredCallback)
+ ->willReturn($view)
+ ;
+
+ $this->assertSame($view, $this->factory->createView($list, ChoiceList::preferred($type, $preferredCallback)));
+ $this->assertSame($view, $this->factory->createView($list, ChoiceList::preferred($type, function () {})));
}
public function testCreateViewDifferentPreferredChoicesClosure()
@@ -311,14 +435,38 @@ public function testCreateViewSameLabelClosure()
$labels = function () {};
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createView')
->with($list, null, $labels)
- ->willReturn($view);
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, null, $labels)
+ ->willReturn($view2)
+ ;
$this->assertSame($view, $this->factory->createView($list, null, $labels));
- $this->assertSame($view, $this->factory->createView($list, null, $labels));
+ $this->assertSame($view2, $this->factory->createView($list, null, $labels));
+ }
+
+ public function testCreateViewSameLabelClosureUseCache()
+ {
+ $labelsCallback = function () {};
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createView')
+ ->with($list, null, $labelsCallback)
+ ->willReturn($view)
+ ;
+
+ $this->assertSame($view, $this->factory->createView($list, null, ChoiceList::label($type, $labelsCallback)));
+ $this->assertSame($view, $this->factory->createView($list, null, ChoiceList::label($type, function () {})));
}
public function testCreateViewDifferentLabelClosure()
@@ -347,14 +495,38 @@ public function testCreateViewSameIndexClosure()
$index = function () {};
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createView')
->with($list, null, null, $index)
- ->willReturn($view);
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, null, null, $index)
+ ->willReturn($view2)
+ ;
$this->assertSame($view, $this->factory->createView($list, null, null, $index));
- $this->assertSame($view, $this->factory->createView($list, null, null, $index));
+ $this->assertSame($view2, $this->factory->createView($list, null, null, $index));
+ }
+
+ public function testCreateViewSameIndexClosureUseCache()
+ {
+ $indexCallback = function () {};
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createView')
+ ->with($list, null, null, $indexCallback)
+ ->willReturn($view)
+ ;
+
+ $this->assertSame($view, $this->factory->createView($list, null, null, ChoiceList::fieldName($type, $indexCallback)));
+ $this->assertSame($view, $this->factory->createView($list, null, null, ChoiceList::fieldName($type, function () {})));
}
public function testCreateViewDifferentIndexClosure()
@@ -383,14 +555,38 @@ public function testCreateViewSameGroupByClosure()
$groupBy = function () {};
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createView')
->with($list, null, null, null, $groupBy)
- ->willReturn($view);
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, null, null, null, $groupBy)
+ ->willReturn($view2)
+ ;
$this->assertSame($view, $this->factory->createView($list, null, null, null, $groupBy));
- $this->assertSame($view, $this->factory->createView($list, null, null, null, $groupBy));
+ $this->assertSame($view2, $this->factory->createView($list, null, null, null, $groupBy));
+ }
+
+ public function testCreateViewSameGroupByClosureUseCache()
+ {
+ $groupByCallback = function () {};
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createView')
+ ->with($list, null, null, null, $groupByCallback)
+ ->willReturn($view)
+ ;
+
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, ChoiceList::groupBy($type, $groupByCallback)));
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, ChoiceList::groupBy($type, function () {})));
}
public function testCreateViewDifferentGroupByClosure()
@@ -419,14 +615,37 @@ public function testCreateViewSameAttributes()
$attr = ['class' => 'foobar'];
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->at(0))
+ ->method('createView')
+ ->with($list, null, null, null, null, $attr)
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, null, null, null, null, $attr)
+ ->willReturn($view2)
+ ;
+
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr));
+ $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr));
+ }
+
+ public function testCreateViewSameAttributesUseCache()
+ {
+ $attr = ['class' => 'foobar'];
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
$this->decoratedFactory->expects($this->once())
->method('createView')
->with($list, null, null, null, null, $attr)
->willReturn($view);
- $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr));
- $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr));
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, null, ChoiceList::attr($type, $attr)));
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, null, ChoiceList::attr($type, ['class' => 'foobar'])));
}
public function testCreateViewDifferentAttributes()
@@ -455,14 +674,37 @@ public function testCreateViewSameAttributesClosure()
$attr = function () {};
$list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock();
$view = new ChoiceListView();
+ $view2 = new ChoiceListView();
- $this->decoratedFactory->expects($this->once())
+ $this->decoratedFactory->expects($this->at(0))
->method('createView')
->with($list, null, null, null, null, $attr)
- ->willReturn($view);
+ ->willReturn($view)
+ ;
+ $this->decoratedFactory->expects($this->at(1))
+ ->method('createView')
+ ->with($list, null, null, null, null, $attr)
+ ->willReturn($view2)
+ ;
$this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr));
- $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr));
+ $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr));
+ }
+
+ public function testCreateViewSameAttributesClosureUseCache()
+ {
+ $attrCallback = function () {};
+ $type = $this->createMock(FormTypeInterface::class);
+ $list = $this->createMock(ChoiceListInterface::class);
+ $view = new ChoiceListView();
+
+ $this->decoratedFactory->expects($this->once())
+ ->method('createView')
+ ->with($list, null, null, null, null, $attrCallback)
+ ->willReturn($view);
+
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, null, ChoiceList::attr($type, $attrCallback)));
+ $this->assertSame($view, $this->factory->createView($list, null, null, null, null, ChoiceList::attr($type, function () {})));
}
public function testCreateViewDifferentAttributesClosure()
diff --git a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
index 0c10fdd5a3769..e59b3108eec05 100644
--- a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
+++ b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
@@ -42,12 +42,6 @@ public function testDebugDeprecatedDefaults()
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertSame(<<setNormalizer('foo', function (Options $options, $value) {
return (string) $value;
});
+ $resolver->setInfo('foo', 'Info');
}
}
diff --git a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
index 15fdd85ff6657..06c39150ef898 100644
--- a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
+++ b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
@@ -218,82 +218,6 @@ public function addTaggedTypeExtensionsDataProvider()
];
}
- /**
- * @group legacy
- * @dataProvider addLegacyTaggedTypeExtensionsDataProvider
- */
- public function testAddLegacyTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions)
- {
- $container = $this->createContainerBuilder();
-
- $container->setDefinition('form.extension', $this->createExtensionDefinition());
-
- foreach ($extensions as $serviceId => $tag) {
- $container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag);
- }
-
- $container->compile();
-
- $extDefinition = $container->getDefinition('form.extension');
- $this->assertEquals($expectedRegisteredExtensions, $extDefinition->getArgument(1));
- }
-
- public function addLegacyTaggedTypeExtensionsDataProvider(): array
- {
- return [
- [
- [
- 'my.type_extension1' => ['extended_type' => 'type1'],
- 'my.type_extension2' => ['extended_type' => 'type1'],
- 'my.type_extension3' => ['extended_type' => 'type2'],
- ],
- [
- 'type1' => new IteratorArgument([
- new Reference('my.type_extension1'),
- new Reference('my.type_extension2'),
- ]),
- 'type2' => new IteratorArgument([new Reference('my.type_extension3')]),
- ],
- ],
- [
- [
- 'my.type_extension1' => ['extended_type' => 'type1', 'priority' => 1],
- 'my.type_extension2' => ['extended_type' => 'type1', 'priority' => 2],
- 'my.type_extension3' => ['extended_type' => 'type1', 'priority' => -1],
- 'my.type_extension4' => ['extended_type' => 'type2', 'priority' => 2],
- 'my.type_extension5' => ['extended_type' => 'type2', 'priority' => 1],
- 'my.type_extension6' => ['extended_type' => 'type2', 'priority' => 1],
- ],
- [
- 'type1' => new IteratorArgument([
- new Reference('my.type_extension2'),
- new Reference('my.type_extension1'),
- new Reference('my.type_extension3'),
- ]),
- 'type2' => new IteratorArgument([
- new Reference('my.type_extension4'),
- new Reference('my.type_extension5'),
- new Reference('my.type_extension6'),
- ]),
- ],
- ],
- ];
- }
-
- public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttributeNorImplementingGetExtendedTypes()
- {
- $this->expectException('InvalidArgumentException');
- $this->expectExceptionMessage('"form.type_extension" tagged services have to implement the static getExtendedTypes() method. Class "stdClass" for service "my.type_extension" does not implement it.');
- $container = $this->createContainerBuilder();
-
- $container->setDefinition('form.extension', $this->createExtensionDefinition());
- $container->register('my.type_extension', 'stdClass')
- ->setPublic(true)
- ->addTag('form.type_extension');
-
- $container->compile();
- }
-
public function testAddTaggedFormTypeExtensionWithoutExtendingAnyType()
{
$this->expectException('InvalidArgumentException');
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
index eacf1d971b905..5645a34c5ac6d 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php
@@ -91,18 +91,6 @@ public function testTransformWithRounding($input, $output, $roundingMode)
$this->assertEquals($output, $transformer->transform($input));
}
- /**
- * @group legacy
- * @expectedDeprecation Passing a precision as the first value to %s::__construct() is deprecated since Symfony 4.2 and support for it will be dropped in 5.0.
- * @dataProvider transformWithRoundingProvider
- */
- public function testTransformWithRoundingUsingLegacyConstructorSignature($input, $output, $roundingMode)
- {
- $transformer = new IntegerToLocalizedStringTransformer(null, null, $roundingMode);
-
- $this->assertEquals($output, $transformer->transform($input));
- }
-
public function testReverseTransform()
{
// Since we test against "de_AT", we need the full implementation
@@ -138,25 +126,6 @@ public function testReverseTransformWithGrouping()
$this->assertEquals(12345, $transformer->reverseTransform('12345'));
}
- /**
- * @group legacy
- * @expectedDeprecation Passing a precision as the first value to %s::__construct() is deprecated since Symfony 4.2 and support for it will be dropped in 5.0.
- */
- public function testReverseTransformWithGroupingUsingLegacyConstructorSignature()
- {
- // Since we test against "de_DE", we need the full implementation
- IntlTestHelper::requireFullIntl($this, false);
-
- \Locale::setDefault('de_DE');
-
- $transformer = new IntegerToLocalizedStringTransformer(null, true);
-
- $this->assertEquals(1234, $transformer->reverseTransform('1.234'));
- $this->assertEquals(12345, $transformer->reverseTransform('12.345'));
- $this->assertEquals(1234, $transformer->reverseTransform('1234'));
- $this->assertEquals(12345, $transformer->reverseTransform('12345'));
- }
-
public function reverseTransformWithRoundingProvider()
{
return [
@@ -218,18 +187,6 @@ public function testReverseTransformWithRounding($input, $output, $roundingMode)
$this->assertEquals($output, $transformer->reverseTransform($input));
}
- /**
- * @group legacy
- * @expectedDeprecation Passing a precision as the first value to %s::__construct() is deprecated since Symfony 4.2 and support for it will be dropped in 5.0.
- * @dataProvider reverseTransformWithRoundingProvider
- */
- public function testReverseTransformWithRoundingUsingLegacyConstructorSignature($input, $output, $roundingMode)
- {
- $transformer = new IntegerToLocalizedStringTransformer(null, null, $roundingMode);
-
- $this->assertEquals($output, $transformer->reverseTransform($input));
- }
-
public function testReverseTransformExpectsString()
{
$this->expectException('Symfony\Component\Form\Exception\TransformationFailedException');
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
index f417c234af4cc..b0617861694ba 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
@@ -2049,4 +2049,45 @@ public function provideTrimCases()
'Multiple expanded' => [true, true],
];
}
+
+ /**
+ * @dataProvider expandedIsEmptyWhenNoRealChoiceIsSelectedProvider
+ */
+ public function testExpandedIsEmptyWhenNoRealChoiceIsSelected(bool $expected, $submittedData, bool $multiple, bool $required, $placeholder)
+ {
+ $options = [
+ 'expanded' => true,
+ 'choices' => [
+ 'foo' => 'bar',
+ ],
+ 'multiple' => $multiple,
+ 'required' => $required,
+ ];
+
+ if (!$multiple) {
+ $options['placeholder'] = $placeholder;
+ }
+
+ $form = $this->factory->create(static::TESTED_TYPE, null, $options);
+
+ $form->submit($submittedData);
+
+ $this->assertSame($expected, $form->isEmpty());
+ }
+
+ public function expandedIsEmptyWhenNoRealChoiceIsSelectedProvider()
+ {
+ // Some invalid cases are voluntarily not tested:
+ // - multiple with placeholder
+ // - required with placeholder
+ return [
+ 'Nothing submitted / single / not required / without a placeholder -> should be empty' => [true, null, false, false, null],
+ 'Nothing submitted / single / not required / with a placeholder -> should not be empty' => [false, null, false, false, 'ccc'], // It falls back on the placeholder
+ 'Nothing submitted / single / required / without a placeholder -> should be empty' => [true, null, false, true, null],
+ 'Nothing submitted / single / required / with a placeholder -> should be empty' => [true, null, false, true, 'ccc'],
+ 'Nothing submitted / multiple / not required / without a placeholder -> should be empty' => [true, null, true, false, null],
+ 'Nothing submitted / multiple / required / without a placeholder -> should be empty' => [true, null, true, true, null],
+ 'Placeholder submitted / single / not required / with a placeholder -> should not be empty' => [false, '', false, false, 'ccc'], // The placeholder is a selected value
+ ];
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php
index ca8ffe3e8c3a4..8f565f772bc78 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
-use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Intl\Util\IntlTestHelper;
class CountryTypeTest extends BaseTypeTest
@@ -117,14 +116,4 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = 'FR', $expectedD
{
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
}
-
- /**
- * @group legacy
- */
- public function testInvalidChoiceValuesAreDropped()
- {
- $type = new CountryType();
-
- $this->assertSame([], $type->loadChoicesForValues(['foo']));
- }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php
index 57a67a292b395..07e212afda153 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
-use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
use Symfony\Component\Intl\Util\IntlTestHelper;
class CurrencyTypeTest extends BaseTypeTest
@@ -62,14 +61,4 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = 'EUR', $expected
{
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
}
-
- /**
- * @group legacy
- */
- public function testInvalidChoiceValuesAreDropped()
- {
- $type = new CurrencyType();
-
- $this->assertSame([], $type->loadChoicesForValues(['foo']));
- }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
index b0c4f3549252d..afa941dccce32 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
@@ -302,6 +302,7 @@ public function testSubmitStringSingleTextWithSeconds()
public function testSubmitDifferentPattern()
{
$form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'html5' => false,
'date_format' => 'MM*yyyy*dd',
'date_widget' => 'single_text',
'time_widget' => 'single_text',
@@ -471,20 +472,6 @@ public function testDontPassHtml5TypeIfHtml5NotAllowed()
$this->assertArrayNotHasKey('type', $view->vars);
}
- /**
- * @group legacy
- */
- public function testDontPassHtml5TypeIfNotHtml5Format()
- {
- $view = $this->factory->create(static::TESTED_TYPE, null, [
- 'widget' => 'single_text',
- 'format' => 'yyyy-MM-dd HH:mm',
- ])
- ->createView();
-
- $this->assertArrayNotHasKey('type', $view->vars);
- }
-
public function testDontPassHtml5TypeIfNotSingleText()
{
$view = $this->factory->create(static::TESTED_TYPE, null, [
@@ -498,6 +485,7 @@ public function testDontPassHtml5TypeIfNotSingleText()
public function testSingleTextWidgetWithCustomNonHtml5Format()
{
$form = $this->factory->create(static::TESTED_TYPE, new \DateTime('2019-02-13 19:12:13'), [
+ 'html5' => false,
'widget' => 'single_text',
'date_format' => \IntlDateFormatter::SHORT,
'format' => null,
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/EmailTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/EmailTypeTest.php
new file mode 100644
index 0000000000000..159c51d44aba7
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/EmailTypeTest.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+class EmailTypeTest extends BaseTypeTest
+{
+ const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\EmailType';
+
+ public function testDefaultInputmode()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE);
+
+ $this->assertSame('email', $form->createView()->vars['attr']['inputmode']);
+ }
+
+ public function testOverwrittenInputmode()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, ['attr' => ['inputmode' => 'text']]);
+
+ $this->assertSame('text', $form->createView()->vars['attr']['inputmode']);
+ }
+
+ public function testSubmitNull($expected = null, $norm = null, $view = null)
+ {
+ parent::testSubmitNull($expected, $norm, '');
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php
index 13c90dd8594f1..488e60bc97e7a 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
-use Symfony\Component\Form\Extension\Core\Type\LanguageType;
+use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Intl\Util\IntlTestHelper;
class LanguageTypeTest extends BaseTypeTest
@@ -87,6 +87,52 @@ public function testChoiceTranslationLocaleAndAlpha3Option()
$this->assertNotContainsEquals(new ChoiceView('my', 'my', 'бірманська'), $choices);
}
+ /**
+ * @requires extension intl
+ */
+ public function testChoiceSelfTranslationOption()
+ {
+ $choices = $this->factory
+ ->create(static::TESTED_TYPE, null, [
+ 'choice_self_translation' => true,
+ ])
+ ->createView()->vars['choices'];
+
+ $this->assertContainsEquals(new ChoiceView('cs', 'cs', 'čeština'), $choices);
+ $this->assertContainsEquals(new ChoiceView('es', 'es', 'español'), $choices);
+ $this->assertContainsEquals(new ChoiceView('fr', 'fr', 'français'), $choices);
+ $this->assertContainsEquals(new ChoiceView('ta', 'ta', 'தமிழ்'), $choices);
+ $this->assertContainsEquals(new ChoiceView('uk', 'uk', 'українська'), $choices);
+ $this->assertContainsEquals(new ChoiceView('yi', 'yi', 'ייִדיש'), $choices);
+ $this->assertContainsEquals(new ChoiceView('zh', 'zh', '中文'), $choices);
+ }
+
+ /**
+ * @requires extension intl
+ */
+ public function testChoiceSelfTranslationAndAlpha3Options()
+ {
+ $choices = $this->factory
+ ->create(static::TESTED_TYPE, null, [
+ 'alpha3' => true,
+ 'choice_self_translation' => true,
+ ])
+ ->createView()->vars['choices'];
+
+ $this->assertContainsEquals(new ChoiceView('spa', 'spa', 'español'), $choices, '', false, false);
+ $this->assertContainsEquals(new ChoiceView('yid', 'yid', 'ייִדיש'), $choices, '', false, false);
+ }
+
+ public function testSelfTranslationNotAllowedWithChoiceTranslation()
+ {
+ $this->expectException(LogicException::class);
+
+ $this->factory->create(static::TESTED_TYPE, null, [
+ 'choice_translation_locale' => 'es',
+ 'choice_self_translation' => true,
+ ]);
+ }
+
public function testMultipleLanguagesIsNotIncluded()
{
$choices = $this->factory->create(static::TESTED_TYPE, 'language')
@@ -104,14 +150,4 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = 'en', $expectedD
{
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
}
-
- /**
- * @group legacy
- */
- public function testInvalidChoiceValuesAreDropped()
- {
- $type = new LanguageType();
-
- $this->assertSame([], $type->loadChoicesForValues(['foo']));
- }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php
index a436b6d347c18..cf18ed2b5bc10 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
-use Symfony\Component\Form\Extension\Core\Type\LocaleType;
use Symfony\Component\Intl\Util\IntlTestHelper;
class LocaleTypeTest extends BaseTypeTest
@@ -62,14 +61,4 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = 'en', $expectedD
{
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
}
-
- /**
- * @group legacy
- */
- public function testInvalidChoiceValuesAreDropped()
- {
- $type = new LocaleType();
-
- $this->assertSame([], $type->loadChoicesForValues(['foo']));
- }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php
index 668018b6b5e15..86cd998ad2c2d 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php
@@ -77,32 +77,26 @@ public function testDefaultFormattingWithScaleAndStringInput(): void
$this->assertSame('12345,68', $form->createView()->vars['value']);
}
- /**
- * @group legacy
- * @expectedDeprecation Using the Symfony\Component\Form\Extension\Core\Type\NumberType with float or int data when the "input" option is set to "string" is deprecated since Symfony 4.4 and will throw an exception in 5.0.
- */
public function testStringInputWithFloatData(): void
{
- $form = $this->factory->create(static::TESTED_TYPE, 12345.6789, [
+ $this->expectException('Symfony\Component\Form\Exception\TransformationFailedException');
+ $this->expectExceptionMessage('Expected a numeric string.');
+
+ $this->factory->create(static::TESTED_TYPE, 12345.6789, [
'input' => 'string',
'scale' => 2,
]);
-
- $this->assertSame('12345,68', $form->createView()->vars['value']);
}
- /**
- * @group legacy
- * @expectedDeprecation Using the Symfony\Component\Form\Extension\Core\Type\NumberType with float or int data when the "input" option is set to "string" is deprecated since Symfony 4.4 and will throw an exception in 5.0.
- */
public function testStringInputWithIntData(): void
{
- $form = $this->factory->create(static::TESTED_TYPE, 12345, [
+ $this->expectException('Symfony\Component\Form\Exception\TransformationFailedException');
+ $this->expectExceptionMessage('Expected a numeric string.');
+
+ $this->factory->create(static::TESTED_TYPE, 12345, [
'input' => 'string',
'scale' => 2,
]);
-
- $this->assertSame('12345,00', $form->createView()->vars['value']);
}
public function testDefaultFormattingWithRounding(): void
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/SearchTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/SearchTypeTest.php
new file mode 100644
index 0000000000000..101b02dab7337
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/SearchTypeTest.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+class SearchTypeTest extends BaseTypeTest
+{
+ const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\SearchType';
+
+ public function testDefaultInputmode()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE);
+
+ $this->assertSame('search', $form->createView()->vars['attr']['inputmode']);
+ }
+
+ public function testOverwrittenInputmode()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, ['attr' => ['inputmode' => 'text']]);
+
+ $this->assertSame('text', $form->createView()->vars['attr']['inputmode']);
+ }
+
+ public function testSubmitNull($expected = null, $norm = null, $view = null)
+ {
+ parent::testSubmitNull($expected, $norm, '');
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TelTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TelTypeTest.php
new file mode 100644
index 0000000000000..a72bd11c6f51d
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TelTypeTest.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\Type;
+
+class TelTypeTest extends BaseTypeTest
+{
+ const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\TelType';
+
+ public function testDefaultInputmode()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE);
+
+ $this->assertSame('tel', $form->createView()->vars['attr']['inputmode']);
+ }
+
+ public function testOverwrittenInputmode()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, ['attr' => ['inputmode' => 'text']]);
+
+ $this->assertSame('text', $form->createView()->vars['attr']['inputmode']);
+ }
+
+ public function testSubmitNull($expected = null, $norm = null, $view = null)
+ {
+ parent::testSubmitNull($expected, $norm, '');
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php
index 645c5b9edee1f..10652b9b98cc7 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php
@@ -426,12 +426,11 @@ public function testSetDataDifferentTimezonesDuringDaylightSavingTime()
$this->assertSame(['hour' => '16', 'minute' => '9', 'second' => '10'], $form->getViewData());
}
- /**
- * @group legacy
- * @expectedDeprecation Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is deprecated since Symfony 4.4.
- */
public function testSetDataDifferentTimezonesWithoutReferenceDate()
{
+ $this->expectException('Symfony\Component\Form\Exception\LogicException');
+ $this->expectExceptionMessage('Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is not supported.');
+
$form = $this->factory->create(static::TESTED_TYPE, null, [
'model_timezone' => 'UTC',
'view_timezone' => 'Europe/Berlin',
@@ -861,6 +860,15 @@ public function testModelTimezoneDefaultToReferenceDateTimezoneIfProvided()
$this->assertSame('Europe/Berlin', $form->getConfig()->getOption('model_timezone'));
}
+ public function testViewTimezoneDefaultsToModelTimezoneIfProvided()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'model_timezone' => 'Europe/Berlin',
+ ]);
+
+ $this->assertSame('Europe/Berlin', $form->getConfig()->getOption('view_timezone'));
+ }
+
public function testPassDefaultChoiceTranslationDomain()
{
$form = $this->factory->create(static::TESTED_TYPE);
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php
index 3f34b2eb5ea1b..699e1d2700195 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php
@@ -72,29 +72,6 @@ public function testDateTimeZoneInputWithBc()
$this->assertContainsEquals('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
}
- /**
- * @group legacy
- * @expectedDeprecation The option "regions" is deprecated since Symfony 4.2.
- */
- public function testFilterByRegions()
- {
- $choices = $this->factory->create(static::TESTED_TYPE, null, ['regions' => \DateTimeZone::EUROPE])
- ->createView()->vars['choices'];
-
- $this->assertContainsEquals(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Europe / Amsterdam'), $choices);
- }
-
- /**
- * @group legacy
- * @expectedDeprecation The option "regions" is deprecated since Symfony 4.2.
- */
- public function testFilterByRegionsWithIntl()
- {
- $this->expectException('Symfony\Component\Form\Exception\LogicException');
- $this->expectExceptionMessage('The "regions" option can only be used if the "intl" option is set to false.');
- $this->factory->create(static::TESTED_TYPE, null, ['regions' => \DateTimeZone::EUROPE, 'intl' => true]);
- }
-
/**
* @requires extension intl
*/
diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php
index 97b89849d19e4..53ecc1cb06349 100644
--- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php
@@ -33,12 +33,9 @@ protected function setUp(): void
$this->extension = new DataCollectorTypeExtension($this->dataCollector);
}
- /**
- * @group legacy
- */
public function testGetExtendedType()
{
- $this->assertEquals('Symfony\Component\Form\Extension\Core\Type\FormType', $this->extension->getExtendedType());
+ $this->assertEquals(['Symfony\Component\Form\Extension\Core\Type\FormType'], $this->extension::getExtendedTypes());
}
public function testBuildForm()
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
index a920e3be5b3ac..b0b945e845ad8 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
@@ -57,15 +57,13 @@ public function testValidConstraint()
public function testGroupSequenceWithConstraintsOption()
{
- $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
-
$form = Forms::createFormFactoryBuilder()
->addExtension(new ValidatorExtension(Validation::createValidator()))
->getFormFactory()
->create(FormTypeTest::TESTED_TYPE, null, (['validation_groups' => new GroupSequence(['First', 'Second'])]))
->add('field', TextTypeTest::TESTED_TYPE, [
'constraints' => [
- new Length(['min' => 10, 'groups' => ['First']] + $allowEmptyString),
+ new Length(['min' => 10, 'allowEmptyString' => true, 'groups' => ['First']]),
new Email(['groups' => ['Second']]),
],
])
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
index d1af4d7d21783..34c911f52a9fa 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
@@ -15,6 +15,7 @@
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class UploadValidatorExtensionTest extends TypeTestCase
@@ -38,23 +39,18 @@ public function testPostMaxSizeTranslation()
}
}
-class DummyTranslator implements TranslatorInterface
+class DummyTranslator implements TranslatorInterface, LocaleAwareInterface
{
public function trans($id, array $parameters = [], $domain = null, $locale = null): string
{
return 'translated max {{ max }}!';
}
- public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
- {
- return 'translated max {{ max }}!';
- }
-
public function setLocale($locale)
{
}
- public function getLocale()
+ public function getLocale(): string
{
return 'en';
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php
index d9f5f8bc298b7..e96c8b60c3929 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php
@@ -62,13 +62,11 @@ protected function setUp(): void
public function guessRequiredProvider()
{
- $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
-
return [
[new NotNull(), new ValueGuess(true, Guess::HIGH_CONFIDENCE)],
[new NotBlank(), new ValueGuess(true, Guess::HIGH_CONFIDENCE)],
[new IsTrue(), new ValueGuess(true, Guess::HIGH_CONFIDENCE)],
- [new Length(['min' => 10, 'max' => 10] + $allowEmptyString), new ValueGuess(false, Guess::LOW_CONFIDENCE)],
+ [new Length(['min' => 10, 'max' => 10, 'allowEmptyString' => true]), new ValueGuess(false, Guess::LOW_CONFIDENCE)],
[new Range(['min' => 1, 'max' => 20]), new ValueGuess(false, Guess::LOW_CONFIDENCE)],
];
}
@@ -104,9 +102,7 @@ public function testGuessMaxLengthForConstraintWithMaxValue()
public function testGuessMaxLengthForConstraintWithMinValue()
{
- $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
-
- $constraint = new Length(['min' => '2'] + $allowEmptyString);
+ $constraint = new Length(['min' => '2', 'allowEmptyString' => true]);
$result = $this->guesser->guessMaxLengthForConstraint($constraint);
$this->assertNull($result);
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
index d02b0c02ae3a2..4a226f576d60f 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
@@ -2,6 +2,8 @@
Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_translation_domain)
=================================================================================
+ ---------------- -----------%s
+ Info - %s
---------------- -----------%s
Required false %s
---------------- -----------%s
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/deprecated_option.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/deprecated_option.txt
index e607f20b73755..b7edd974bb371 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/deprecated_option.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/deprecated_option.txt
@@ -5,6 +5,8 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (bar)
Deprecated true
--------------------- -----------------------------------
Deprecation message "The option "bar" is deprecated."
+ --------------------- -----------------------------------
+ Info -
--------------------- -----------------------------------
Required false
--------------------- -----------------------------------
@@ -15,4 +17,4 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (bar)
Allowed values -
--------------------- -----------------------------------
Normalizers -
- --------------------- -----------------------------------
\ No newline at end of file
+ --------------------- -----------------------------------
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
index b184d75a448e2..043a1dbc27399 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
@@ -2,6 +2,8 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (empty_data)
====================================================================
---------------- ----------------------%s
+ Info - %s
+ ---------------- ----------------------%s
Required false %s
---------------- ----------------------%s
Default Value: null %s
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
index b42c10f5bd790..fd2f8f3f19c20 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
@@ -2,6 +2,8 @@
Symfony\Component\Form\Tests\Console\Descriptor\FooType (foo)
=============================================================
+ ---------------- -----------%s
+ Info - %s
---------------- -----------%s
Required true %s
---------------- -----------%s
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json
index 6b1204c6b8dab..e02e66731894e 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json
@@ -42,6 +42,7 @@
"help_html",
"help_translation_parameters",
"inherit_data",
+ "is_empty_callback",
"label",
"label_attr",
"label_format",
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
index 6c6d38628d293..bc56245a992cf 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
@@ -22,6 +22,7 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
help_html
help_translation_parameters
inherit_data
+ is_empty_callback
label
label_attr
label_format
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json
index 5eaf65b86377e..9d1058b5882ae 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json
@@ -22,6 +22,7 @@
"help_html",
"help_translation_parameters",
"inherit_data",
+ "is_empty_callback",
"label",
"label_attr",
"label_format",
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt
index 2007781f2dcca..e8f9b2660c0f5 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt
@@ -24,6 +24,7 @@ Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form")
help_html
help_translation_parameters
inherit_data
+ is_empty_callback
label
label_attr
label_format
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php
index e25d84c8bd748..20fe789cd7dd9 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Form\Tests\Fixtures;
use Symfony\Component\Form\AbstractTypeExtension;
-use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
+use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\OptionsResolver\OptionsResolver;
class LazyChoiceTypeExtension extends AbstractTypeExtension
@@ -24,7 +24,7 @@ class LazyChoiceTypeExtension extends AbstractTypeExtension
*/
public function configureOptions(OptionsResolver $resolver)
{
- $resolver->setDefault('choice_loader', new CallbackChoiceLoader(function () {
+ $resolver->setDefault('choice_loader', ChoiceList::lazy($this, function () {
return [
'Lazy A' => 'lazy_a',
'Lazy B' => 'lazy_b',
diff --git a/src/Symfony/Component/Form/Tests/FormBuilderTest.php b/src/Symfony/Component/Form/Tests/FormBuilderTest.php
index cf5ba94aeeacb..63994f1b83902 100644
--- a/src/Symfony/Component/Form/Tests/FormBuilderTest.php
+++ b/src/Symfony/Component/Form/Tests/FormBuilderTest.php
@@ -55,12 +55,6 @@ public function testAddNameNoStringAndNoInteger()
$this->builder->add(true);
}
- public function testAddTypeNoString()
- {
- $this->expectException('Symfony\Component\Form\Exception\UnexpectedTypeException');
- $this->builder->add('foo', 1234);
- }
-
public function testAddWithGuessFluent()
{
$this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory);
diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php
index 0206aa0f78875..3b6fd1f79d003 100644
--- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php
+++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php
@@ -149,20 +149,6 @@ public function testCreateNamedBuilderDoesNotOverrideExistingDataOption()
$this->assertSame($this->builder, $this->factory->createNamedBuilder('name', 'type', 'DATA', $options));
}
- public function testCreateNamedBuilderThrowsUnderstandableException()
- {
- $this->expectException('Symfony\Component\Form\Exception\UnexpectedTypeException');
- $this->expectExceptionMessage('Expected argument of type "string", "stdClass" given');
- $this->factory->createNamedBuilder('name', new \stdClass());
- }
-
- public function testCreateThrowsUnderstandableException()
- {
- $this->expectException('Symfony\Component\Form\Exception\UnexpectedTypeException');
- $this->expectExceptionMessage('Expected argument of type "string", "stdClass" given');
- $this->factory->create(new \stdClass());
- }
-
public function testCreateUsesBlockPrefixIfTypeGivenAsString()
{
$options = ['a' => '1', 'b' => '2'];
diff --git a/src/Symfony/Component/Form/Tests/FormRendererTest.php b/src/Symfony/Component/Form/Tests/FormRendererTest.php
index 452bb71e8905b..8c85fd84ffbe0 100644
--- a/src/Symfony/Component/Form/Tests/FormRendererTest.php
+++ b/src/Symfony/Component/Form/Tests/FormRendererTest.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\Form\Tests;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Form\FormRenderer;
+use Symfony\Component\Form\FormView;
class FormRendererTest extends TestCase
{
@@ -26,4 +28,18 @@ public function testHumanize()
$this->assertEquals('Is active', $renderer->humanize('is_active'));
$this->assertEquals('Is active', $renderer->humanize('isActive'));
}
+
+ public function testRenderARenderedField()
+ {
+ $this->expectException('Symfony\Component\Form\Exception\BadMethodCallException');
+ $this->expectExceptionMessage('Field "foo" has already been rendered, save the result of previous render call to a variable and output that instead.');
+
+ $formView = new FormView();
+ $formView->vars['name'] = 'foo';
+ $formView->setRendered();
+
+ $engine = $this->getMockBuilder('Symfony\Component\Form\FormRendererEngineInterface')->getMock();
+ $renderer = new FormRenderer($engine);
+ $renderer->searchAndRenderBlock($formView, 'row');
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/MultipleTypesExtension.php b/src/Symfony/Component/Form/Tests/MultipleTypesExtension.php
deleted file mode 100644
index 3eca855f5e9e9..0000000000000
--- a/src/Symfony/Component/Form/Tests/MultipleTypesExtension.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Tests;
-
-use Symfony\Component\Form\AbstractTypeExtension;
-use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
-use Symfony\Component\Form\Extension\Core\Type\DateType;
-
-class MultipleTypesExtension extends AbstractTypeExtension
-{
- public static function getExtendedTypes(): iterable
- {
- yield DateTimeType::class;
- yield DateType::class;
- }
-}
diff --git a/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php b/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php
index 4a1affd848175..e4c4c97485df2 100644
--- a/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php
@@ -392,7 +392,7 @@ private function getMockFormType($typeClass = 'Symfony\Component\Form\AbstractTy
private function getMockFormTypeExtension(): MockObject
{
- return $this->getMockBuilder('Symfony\Component\Form\AbstractTypeExtension')->setMethods(['getExtendedType', 'configureOptions', 'finishView', 'buildView', 'buildForm'])->getMock();
+ return $this->getMockBuilder('Symfony\Component\Form\AbstractTypeExtension')->setMethods(['getExtendedTypes', 'configureOptions', 'finishView', 'buildView', 'buildForm'])->getMock();
}
private function getMockFormFactory(): MockObject
diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php
index 949885222e910..bd71eebfc836d 100644
--- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php
+++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php
@@ -1097,6 +1097,21 @@ public function testCannotCallGetViewDataInPreSetDataListener()
$form->setData('foo');
}
+ public function testIsEmptyCallback()
+ {
+ $config = new FormConfigBuilder('foo', null, $this->dispatcher);
+
+ $config->setIsEmptyCallback(function ($modelData): bool { return 'ccc' === $modelData; });
+ $form = new Form($config);
+ $form->setData('ccc');
+ $this->assertTrue($form->isEmpty());
+
+ $config->setIsEmptyCallback(function (): bool { return false; });
+ $form = new Form($config);
+ $form->setData(null);
+ $this->assertFalse($form->isEmpty());
+ }
+
protected function createForm(): FormInterface
{
return $this->getBuilder()->getForm();
diff --git a/src/Symfony/Component/Form/Tests/TypeExtensionWithoutExtendedTypes.php b/src/Symfony/Component/Form/Tests/TypeExtensionWithoutExtendedTypes.php
deleted file mode 100644
index edad8fc7322b1..0000000000000
--- a/src/Symfony/Component/Form/Tests/TypeExtensionWithoutExtendedTypes.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Tests;
-
-use Symfony\Component\Form\AbstractTypeExtension;
-
-class TypeExtensionWithoutExtendedTypes extends AbstractTypeExtension
-{
-}
diff --git a/src/Symfony/Component/Form/Util/OptionsResolverWrapper.php b/src/Symfony/Component/Form/Util/OptionsResolverWrapper.php
index fbf19ab638ae8..b5f29ac5100f2 100644
--- a/src/Symfony/Component/Form/Util/OptionsResolverWrapper.php
+++ b/src/Symfony/Component/Form/Util/OptionsResolverWrapper.php
@@ -27,7 +27,7 @@ class OptionsResolverWrapper extends OptionsResolver
/**
* @return $this
*/
- public function setNormalizer($option, \Closure $normalizer): self
+ public function setNormalizer(string $option, \Closure $normalizer): self
{
try {
parent::setNormalizer($option, $normalizer);
@@ -41,7 +41,7 @@ public function setNormalizer($option, \Closure $normalizer): self
/**
* @return $this
*/
- public function setAllowedValues($option, $allowedValues): self
+ public function setAllowedValues(string $option, $allowedValues): self
{
try {
parent::setAllowedValues($option, $allowedValues);
@@ -55,7 +55,7 @@ public function setAllowedValues($option, $allowedValues): self
/**
* @return $this
*/
- public function addAllowedValues($option, $allowedValues): self
+ public function addAllowedValues(string $option, $allowedValues): self
{
try {
parent::addAllowedValues($option, $allowedValues);
@@ -69,7 +69,7 @@ public function addAllowedValues($option, $allowedValues): self
/**
* @return $this
*/
- public function setAllowedTypes($option, $allowedTypes): self
+ public function setAllowedTypes(string $option, $allowedTypes): self
{
try {
parent::setAllowedTypes($option, $allowedTypes);
@@ -83,7 +83,7 @@ public function setAllowedTypes($option, $allowedTypes): self
/**
* @return $this
*/
- public function addAllowedTypes($option, $allowedTypes): self
+ public function addAllowedTypes(string $option, $allowedTypes): self
{
try {
parent::addAllowedTypes($option, $allowedTypes);
diff --git a/src/Symfony/Component/Form/Util/StringUtil.php b/src/Symfony/Component/Form/Util/StringUtil.php
index ce507e9ee21f8..7357cc51b9a29 100644
--- a/src/Symfony/Component/Form/Util/StringUtil.php
+++ b/src/Symfony/Component/Form/Util/StringUtil.php
@@ -27,11 +27,9 @@ private function __construct()
/**
* Returns the trimmed data.
*
- * @param string $string
- *
* @return string
*/
- public static function trim($string)
+ public static function trim(string $string)
{
if (null !== $result = @preg_replace('/^[\pZ\p{Cc}]+|[\pZ\p{Cc}]+$/u', '', $string)) {
return $result;
@@ -47,7 +45,7 @@ public static function trim($string)
*
* @return string|null The block prefix or null if not a valid FQCN
*/
- public static function fqcnToBlockPrefix($fqcn)
+ public static function fqcnToBlockPrefix(string $fqcn)
{
// Non-greedy ("+?") to match "type" suffix, if present
if (preg_match('~([^\\\\]+?)(type)?$~i', $fqcn, $matches)) {
diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json
index a3dbf875006ca..92b7138010640 100644
--- a/src/Symfony/Component/Form/composer.json
+++ b/src/Symfony/Component/Form/composer.json
@@ -16,37 +16,38 @@
}
],
"require": {
- "php": "^7.1.3",
- "symfony/event-dispatcher": "^4.3",
+ "php": "^7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
+ "symfony/event-dispatcher": "^4.4|^5.0",
"symfony/intl": "^4.4|^5.0",
- "symfony/options-resolver": "~4.3|^5.0",
+ "symfony/options-resolver": "^5.1",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0",
- "symfony/property-access": "^3.4|^4.0|^5.0",
+ "symfony/property-access": "^5.0",
"symfony/service-contracts": "^1.1|^2"
},
"require-dev": {
"doctrine/collections": "~1.0",
- "symfony/validator": "^3.4.31|^4.3.4|^5.0",
- "symfony/dependency-injection": "^3.4|^4.0|^5.0",
- "symfony/config": "^3.4|^4.0|^5.0",
- "symfony/console": "^4.3|^5.0",
- "symfony/http-foundation": "^3.4|^4.0|^5.0",
- "symfony/http-kernel": "^4.4",
- "symfony/security-csrf": "^3.4|^4.0|^5.0",
- "symfony/translation": "^4.2|^5.0",
- "symfony/var-dumper": "^4.3|^5.0"
+ "symfony/validator": "^4.4|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/config": "^4.4|^5.0",
+ "symfony/console": "^4.4|^5.0",
+ "symfony/http-foundation": "^4.4|^5.0",
+ "symfony/http-kernel": "^4.4|^5.0",
+ "symfony/security-csrf": "^4.4|^5.0",
+ "symfony/translation": "^4.4|^5.0",
+ "symfony/var-dumper": "^4.4|^5.0"
},
"conflict": {
- "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
- "symfony/console": "<4.3",
- "symfony/dependency-injection": "<3.4",
- "symfony/doctrine-bridge": "<3.4",
- "symfony/framework-bundle": "<3.4",
+ "phpunit/phpunit": "<5.4.3",
+ "symfony/console": "<4.4",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/doctrine-bridge": "<4.4",
+ "symfony/framework-bundle": "<4.4",
"symfony/http-kernel": "<4.4",
- "symfony/intl": "<4.3",
- "symfony/translation": "<4.2",
- "symfony/twig-bridge": "<3.4.5|<4.0.5,>=4.0"
+ "symfony/intl": "<4.4",
+ "symfony/translation": "<4.4",
+ "symfony/twig-bridge": "<4.4"
},
"suggest": {
"symfony/validator": "For form validation.",
@@ -62,7 +63,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php
new file mode 100644
index 0000000000000..c62b847910c23
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php
@@ -0,0 +1,163 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient;
+
+use Amp\CancelledException;
+use Amp\Http\Client\DelegateHttpClient;
+use Amp\Http\Client\InterceptedHttpClient;
+use Amp\Http\Client\PooledHttpClient;
+use Amp\Http\Client\Request;
+use Amp\Http\Tunnel\Http1TunnelConnector;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
+use Symfony\Component\HttpClient\Exception\TransportException;
+use Symfony\Component\HttpClient\Internal\AmpClientState;
+use Symfony\Component\HttpClient\Response\AmpResponse;
+use Symfony\Component\HttpClient\Response\ResponseStream;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+use Symfony\Contracts\HttpClient\ResponseStreamInterface;
+use Symfony\Contracts\Service\ResetInterface;
+
+if (!interface_exists(DelegateHttpClient::class)) {
+ throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client".');
+}
+
+/**
+ * A portable implementation of the HttpClientInterface contracts based on Amp's HTTP client.
+ *
+ * @author Nicolas Grekas
+ */
+final class AmpHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
+{
+ use HttpClientTrait;
+ use LoggerAwareTrait;
+
+ private $defaultOptions = self::OPTIONS_DEFAULTS;
+
+ /** @var AmpClientState */
+ private $multi;
+
+ /**
+ * @param array $defaultOptions Default requests' options
+ * @param callable $clientConfigurator A callable that builds a {@see DelegateHttpClient} from a {@see PooledHttpClient};
+ * passing null builds an {@see InterceptedHttpClient} with 2 retries on failures
+ * @param int $maxHostConnections The maximum number of connections to a single host
+ * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue
+ *
+ * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
+ */
+ public function __construct(array $defaultOptions = [], callable $clientConfigurator = null, int $maxHostConnections = 6, int $maxPendingPushes = 50)
+ {
+ $this->defaultOptions['buffer'] = $this->defaultOptions['buffer'] ?? \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
+
+ if ($defaultOptions) {
+ [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
+ }
+
+ $this->multi = new AmpClientState($clientConfigurator, $maxHostConnections, $maxPendingPushes, $this->logger);
+ }
+
+ /**
+ * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
+ *
+ * {@inheritdoc}
+ */
+ public function request(string $method, string $url, array $options = []): ResponseInterface
+ {
+ [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
+
+ $options['proxy'] = self::getProxy($options['proxy'], $url, $options['no_proxy']);
+
+ if (null !== $options['proxy'] && !class_exists(Http1TunnelConnector::class)) {
+ throw new \LogicException('You cannot use the "proxy" option as the "amphp/http-tunnel" package is not installed. Try running "composer require amphp/http-tunnel".');
+ }
+
+ if ('' !== $options['body'] && 'POST' === $method && !isset($options['normalized_headers']['content-type'])) {
+ $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
+ }
+
+ if (!isset($options['normalized_headers']['user-agent'])) {
+ $options['headers'][] = 'User-Agent: Symfony HttpClient/Amp';
+ }
+
+ if (0 < $options['max_duration']) {
+ $options['timeout'] = min($options['max_duration'], $options['timeout']);
+ }
+
+ if ($options['resolve']) {
+ $this->multi->dnsCache = $options['resolve'] + $this->multi->dnsCache;
+ }
+
+ if ($options['peer_fingerprint'] && !isset($options['peer_fingerprint']['pin-sha256'])) {
+ throw new TransportException(__CLASS__.' supports only "pin-sha256" fingerprints.');
+ }
+
+ $request = new Request(implode('', $url), $method);
+
+ if ($options['http_version']) {
+ switch ((float) $options['http_version']) {
+ case 1.0: $request->setProtocolVersions(['1.0']); break;
+ case 1.1: $request->setProtocolVersions(['1.1', '1.0']); break;
+ default: $request->setProtocolVersions(['2', '1.1', '1.0']); break;
+ }
+ }
+
+ foreach ($options['headers'] as $v) {
+ $h = explode(': ', $v, 2);
+ $request->addHeader($h[0], $h[1]);
+ }
+
+ $request->setTcpConnectTimeout(1000 * $options['timeout']);
+ $request->setTlsHandshakeTimeout(1000 * $options['timeout']);
+ $request->setTransferTimeout(1000 * $options['max_duration']);
+
+ if ('' !== $request->getUri()->getUserInfo() && !$request->hasHeader('authorization')) {
+ $auth = explode(':', $request->getUri()->getUserInfo(), 2);
+ $auth = array_map('rawurldecode', $auth) + [1 => ''];
+ $request->setHeader('Authorization', 'Basic '.base64_encode(implode(':', $auth)));
+ }
+
+ return new AmpResponse($this->multi, $request, $options, $this->logger);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stream($responses, float $timeout = null): ResponseStreamInterface
+ {
+ if ($responses instanceof AmpResponse) {
+ $responses = [$responses];
+ } elseif (!is_iterable($responses)) {
+ throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of AmpResponse objects, %s given.', __METHOD__, \is_object($responses) ? \get_class($responses) : \gettype($responses)));
+ }
+
+ return new ResponseStream(AmpResponse::stream($responses, $timeout));
+ }
+
+ public function reset()
+ {
+ $this->multi->dnsCache = [];
+
+ foreach ($this->multi->pushedResponses as $authority => $pushedResponses) {
+ foreach ($pushedResponses as [$pushedUrl, $pushDeferred]) {
+ $pushDeferred->fail(new CancelledException());
+
+ if ($this->logger) {
+ $this->logger->debug(sprintf('Unused pushed response: "%s"', $pushedUrl));
+ }
+ }
+ }
+
+ $this->multi->pushedResponses = [];
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md
index 95b6b10d88f2c..2c4b7069142c0 100644
--- a/src/Symfony/Component/HttpClient/CHANGELOG.md
+++ b/src/Symfony/Component/HttpClient/CHANGELOG.md
@@ -1,6 +1,13 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * added `NoPrivateNetworkHttpClient` decorator
+ * added `AmpHttpClient`, a portable HTTP/2 implementation based on Amp
+ * added `LoggerAwareInterface` to `ScopingHttpClient` and `TraceableHttpClient`
+
4.4.0
-----
diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
index 9247b7479a4e1..7a60bdd435ac8 100644
--- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
+++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
@@ -16,6 +16,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
+use Symfony\Component\VarDumper\Caster\ImgStub;
/**
* @author Jérémy Romey
@@ -34,10 +35,8 @@ public function registerClient(string $name, TraceableHttpClient $client)
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
$this->reset();
@@ -130,8 +129,31 @@ private function collectOnClient(TraceableHttpClient $client): array
}
}
+ if (\is_string($content = $trace['content'])) {
+ $contentType = 'application/octet-stream';
+
+ foreach ($info['response_headers'] ?? [] as $h) {
+ if (0 === stripos($h, 'content-type: ')) {
+ $contentType = substr($h, \strlen('content-type: '));
+ break;
+ }
+ }
+
+ if (0 === strpos($contentType, 'image/') && class_exists(ImgStub::class)) {
+ $content = new ImgStub($content, $contentType, '');
+ } else {
+ $content = [$content];
+ }
+
+ $content = ['response_content' => $content];
+ } elseif (\is_array($content)) {
+ $content = ['response_json' => $content];
+ } else {
+ $content = [];
+ }
+
$debugInfo = array_diff_key($info, $baseInfo);
- $info = array_diff_key($info, $debugInfo) + ['debug_info' => $debugInfo];
+ $info = ['info' => $debugInfo] + array_diff_key($info, $debugInfo) + $content;
unset($traces[$i]['info']); // break PHP reference used by TraceableHttpClient
$traces[$i]['info'] = $this->cloneVar($info);
$traces[$i]['options'] = $this->cloneVar($trace['options']);
diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php
index 3eeb52fdd5cea..0a25306cb7608 100644
--- a/src/Symfony/Component/HttpClient/HttpClientTrait.php
+++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpClient;
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
+use Symfony\Component\HttpClient\Exception\TransportException;
/**
* Provides the common logic from writing HttpClientInterface implementations.
@@ -271,8 +272,31 @@ private static function normalizeBody($body)
return http_build_query($body, '', '&', PHP_QUERY_RFC1738);
}
+ if (\is_string($body)) {
+ return $body;
+ }
+
+ $generatorToCallable = static function (\Generator $body): \Closure {
+ return static function () use ($body) {
+ while ($body->valid()) {
+ $chunk = $body->current();
+ $body->next();
+
+ if ('' !== $chunk) {
+ return $chunk;
+ }
+ }
+
+ return '';
+ };
+ };
+
+ if ($body instanceof \Generator) {
+ return $generatorToCallable($body);
+ }
+
if ($body instanceof \Traversable) {
- $body = function () use ($body) { yield from $body; };
+ return $generatorToCallable((static function ($body) { yield from $body; })($body));
}
if ($body instanceof \Closure) {
@@ -281,24 +305,14 @@ private static function normalizeBody($body)
if ($r->isGenerator()) {
$body = $body(self::$CHUNK_SIZE);
- $body = function () use ($body) {
- while ($body->valid()) {
- $chunk = $body->current();
- $body->next();
-
- if ('' !== $chunk) {
- return $chunk;
- }
- }
- return '';
- };
+ return $generatorToCallable($body);
}
return $body;
}
- if (!\is_string($body) && !\is_array(@stream_get_meta_data($body))) {
+ if (!\is_array(@stream_get_meta_data($body))) {
throw new InvalidArgumentException(sprintf('Option "body" must be string, stream resource, iterable or callable, %s given.', \is_resource($body) ? get_resource_type($body) : \gettype($body)));
}
@@ -541,6 +555,48 @@ private static function mergeQueryString(?string $queryString, array $queryArray
return implode('&', $replace ? array_replace($query, $queryArray) : ($query + $queryArray));
}
+ /**
+ * Loads proxy configuration from the same environment variables as curl when no proxy is explicitly set.
+ */
+ private static function getProxy(?string $proxy, array $url, ?string $noProxy): ?array
+ {
+ if (null === $proxy) {
+ // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities
+ $proxy = $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null;
+
+ if ('https:' === $url['scheme']) {
+ $proxy = $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? $proxy;
+ }
+ }
+
+ if (null === $proxy) {
+ return null;
+ }
+
+ $proxy = (parse_url($proxy) ?: []) + ['scheme' => 'http'];
+
+ if (!isset($proxy['host'])) {
+ throw new TransportException('Invalid HTTP proxy: host is missing.');
+ }
+
+ if ('http' === $proxy['scheme']) {
+ $proxyUrl = 'tcp://'.$proxy['host'].':'.($proxy['port'] ?? '80');
+ } elseif ('https' === $proxy['scheme']) {
+ $proxyUrl = 'ssl://'.$proxy['host'].':'.($proxy['port'] ?? '443');
+ } else {
+ throw new TransportException(sprintf('Unsupported proxy scheme "%s": "http" or "https" expected.', $proxy['scheme']));
+ }
+
+ $noProxy = $noProxy ?? $_SERVER['no_proxy'] ?? $_SERVER['NO_PROXY'] ?? '';
+ $noProxy = $noProxy ? preg_split('/[\s,]+/', $noProxy) : [];
+
+ return [
+ 'url' => $proxyUrl,
+ 'auth' => isset($proxy['user']) ? 'Basic '.base64_encode(rawurldecode($proxy['user']).':'.rawurldecode($proxy['pass'] ?? '')) : null,
+ 'no_proxy' => $noProxy,
+ ];
+ }
+
private static function shouldBuffer(array $headers): bool
{
if (null === $contentType = $headers['content-type'][0] ?? null) {
diff --git a/src/Symfony/Component/HttpClient/Internal/AmpBody.php b/src/Symfony/Component/HttpClient/Internal/AmpBody.php
new file mode 100644
index 0000000000000..6f820e932e7f7
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Internal/AmpBody.php
@@ -0,0 +1,141 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Internal;
+
+use Amp\ByteStream\InputStream;
+use Amp\ByteStream\ResourceInputStream;
+use Amp\Http\Client\RequestBody;
+use Amp\Promise;
+use Amp\Success;
+use Symfony\Component\HttpClient\Exception\TransportException;
+
+/**
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+class AmpBody implements RequestBody, InputStream
+{
+ private $body;
+ private $onProgress;
+ private $offset = 0;
+ private $length = -1;
+ private $uploaded;
+
+ public function __construct($body, &$info, \Closure $onProgress)
+ {
+ $this->body = $body;
+ $this->info = &$info;
+ $this->onProgress = $onProgress;
+
+ if (\is_resource($body)) {
+ $this->offset = ftell($body);
+ $this->length = fstat($body)['size'];
+ $this->body = new ResourceInputStream($body);
+ } elseif (\is_string($body)) {
+ $this->length = \strlen($body);
+ }
+ }
+
+ public function createBodyStream(): InputStream
+ {
+ if (null !== $this->uploaded) {
+ $this->uploaded = null;
+
+ if (\is_string($this->body)) {
+ $this->offset = 0;
+ } elseif ($this->body instanceof ResourceInputStream) {
+ fseek($this->body->getResource(), $this->offset);
+ }
+ }
+
+ return $this;
+ }
+
+ public function getHeaders(): Promise
+ {
+ return new Success([]);
+ }
+
+ public function getBodyLength(): Promise
+ {
+ return new Success($this->length - $this->offset);
+ }
+
+ public function read(): Promise
+ {
+ $this->info['size_upload'] += $this->uploaded;
+ $this->uploaded = 0;
+ ($this->onProgress)();
+
+ $chunk = $this->doRead();
+ $chunk->onResolve(function ($e, $data) {
+ if (null !== $data) {
+ $this->uploaded = \strlen($data);
+ } else {
+ $this->info['upload_content_length'] = $this->info['size_upload'];
+ }
+ });
+
+ return $chunk;
+ }
+
+ public static function rewind(RequestBody $body): RequestBody
+ {
+ if (!$body instanceof self) {
+ return $body;
+ }
+
+ $body->uploaded = null;
+
+ if ($body->body instanceof ResourceInputStream) {
+ fseek($body->body->getResource(), $body->offset);
+
+ return new $body($body->body, $body->info, $body->onProgress);
+ }
+
+ if (\is_string($body->body)) {
+ $body->offset = 0;
+ }
+
+ return $body;
+ }
+
+ private function doRead(): Promise
+ {
+ if ($this->body instanceof ResourceInputStream) {
+ return $this->body->read();
+ }
+
+ if (null === $this->offset || !$this->length) {
+ return new Success();
+ }
+
+ if (\is_string($this->body)) {
+ $this->offset = null;
+
+ return new Success($this->body);
+ }
+
+ if ('' === $data = ($this->body)(16372)) {
+ $this->offset = null;
+
+ return new Success();
+ }
+
+ if (!\is_string($data)) {
+ throw new TransportException(sprintf('Return value of the "body" option callback must be string, %s returned.', \gettype($data)));
+ }
+
+ return new Success($data);
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Internal/AmpClientState.php b/src/Symfony/Component/HttpClient/Internal/AmpClientState.php
new file mode 100644
index 0000000000000..6fa8a2fc20e59
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Internal/AmpClientState.php
@@ -0,0 +1,215 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Internal;
+
+use Amp\CancellationToken;
+use Amp\Deferred;
+use Amp\Http\Client\Connection\ConnectionLimitingPool;
+use Amp\Http\Client\Connection\DefaultConnectionFactory;
+use Amp\Http\Client\InterceptedHttpClient;
+use Amp\Http\Client\Interceptor\RetryRequests;
+use Amp\Http\Client\PooledHttpClient;
+use Amp\Http\Client\Request;
+use Amp\Http\Client\Response;
+use Amp\Http\Tunnel\Http1TunnelConnector;
+use Amp\Http\Tunnel\Https1TunnelConnector;
+use Amp\Promise;
+use Amp\Socket\Certificate;
+use Amp\Socket\ClientTlsContext;
+use Amp\Socket\ConnectContext;
+use Amp\Socket\Connector;
+use Amp\Socket\DnsConnector;
+use Amp\Socket\SocketAddress;
+use Amp\Success;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Internal representation of the Amp client's state.
+ *
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+final class AmpClientState extends ClientState
+{
+ public $dnsCache = [];
+ public $responseCount = 0;
+ public $pushedResponses = [];
+
+ private $clients = [];
+ private $clientConfigurator;
+ private $maxHostConnections;
+ private $maxPendingPushes;
+ private $logger;
+
+ public function __construct(?callable $clientConfigurator, int $maxHostConnections, int $maxPendingPushes, ?LoggerInterface &$logger)
+ {
+ $this->clientConfigurator = $clientConfigurator ?? static function (PooledHttpClient $client) {
+ return new InterceptedHttpClient($client, new RetryRequests(2));
+ };
+ $this->maxHostConnections = $maxHostConnections;
+ $this->maxPendingPushes = $maxPendingPushes;
+ $this->logger = &$logger;
+ }
+
+ /**
+ * @return Promise
+ */
+ public function request(array $options, Request $request, CancellationToken $cancellation, array &$info, \Closure $onProgress, &$handle): Promise
+ {
+ if ($options['proxy']) {
+ if ($request->hasHeader('proxy-authorization')) {
+ $options['proxy']['auth'] = $request->getHeader('proxy-authorization');
+ }
+
+ // Matching "no_proxy" should follow the behavior of curl
+ $host = $request->getUri()->getHost();
+ foreach ($options['proxy']['no_proxy'] as $rule) {
+ $dotRule = '.'.ltrim($rule, '.');
+
+ if ('*' === $rule || $host === $rule || substr($host, -\strlen($dotRule)) === $dotRule) {
+ $options['proxy'] = null;
+ break;
+ }
+ }
+ }
+
+ $request = clone $request;
+
+ if ($request->hasHeader('proxy-authorization')) {
+ $request->removeHeader('proxy-authorization');
+ }
+
+ if ($options['capture_peer_cert_chain']) {
+ $info['peer_certificate_chain'] = [];
+ }
+
+ $request->addEventListener(new AmpListener($info, $options['peer_fingerprint']['pin-sha256'] ?? [], $onProgress, $handle));
+ $request->setPushHandler(function ($request, $response) use ($options): Promise {
+ return $this->handlePush($request, $response, $options);
+ });
+
+ ($request->hasHeader('content-length') ? new Success((int) $request->getHeader('content-length')) : $request->getBody()->getBodyLength())
+ ->onResolve(static function ($e, $bodySize) use (&$info) {
+ if (null !== $bodySize && 0 <= $bodySize) {
+ $info['upload_content_length'] = ((1 + $info['upload_content_length']) ?? 1) - 1 + $bodySize;
+ }
+ });
+
+ [$client, $connector] = $this->getClient($options);
+ $response = $client->request($request, $cancellation);
+ $response->onResolve(static function ($e) use ($connector, &$handle) {
+ if (null === $e) {
+ $handle = $connector->handle;
+ }
+ });
+
+ return $response;
+ }
+
+ private function getClient(array $options): array
+ {
+ $options = [
+ 'bindto' => $options['bindto'] ?: '0',
+ 'verify_peer' => $options['verify_peer'],
+ 'capath' => $options['capath'],
+ 'cafile' => $options['cafile'],
+ 'local_cert' => $options['local_cert'],
+ 'local_pk' => $options['local_pk'],
+ 'ciphers' => $options['ciphers'],
+ 'capture_peer_cert_chain' => $options['capture_peer_cert_chain'] || $options['peer_fingerprint'],
+ 'proxy' => $options['proxy'],
+ ];
+
+ $key = md5(serialize($options));
+
+ if (isset($this->clients[$key])) {
+ return $this->clients[$key];
+ }
+
+ $context = new ClientTlsContext('');
+ $options['verify_peer'] || $context = $context->withoutPeerVerification();
+ $options['cafile'] && $context = $context->withCaFile($options['cafile']);
+ $options['capath'] && $context = $context->withCaPath($options['capath']);
+ $options['local_cert'] && $context = $context->withCertificate(new Certificate($options['local_cert'], $options['local_pk']));
+ $options['ciphers'] && $context = $context->withCiphers($options['ciphers']);
+ $options['capture_peer_cert_chain'] && $context = $context->withPeerCapturing();
+
+ $connector = $handleConnector = new class() implements Connector {
+ public $connector;
+ public $uri;
+ public $handle;
+
+ public function connect(string $uri, ?ConnectContext $context = null, ?CancellationToken $token = null): Promise
+ {
+ $result = $this->connector->connect($this->uri ?? $uri, $context, $token);
+ $result->onResolve(function ($e, $socket) {
+ $this->handle = null !== $socket ? $socket->getResource() : false;
+ });
+
+ return $result;
+ }
+ };
+ $connector->connector = new DnsConnector(new AmpResolver($this->dnsCache));
+
+ $context = (new ConnectContext())->withTlsContext($context);
+
+ if ($options['bindto']) {
+ if (file_exists($options['bindto'])) {
+ $connector->uri = 'unix://'.$options['bindto'];
+ } else {
+ $context = $context->withBindTo($options['bindto']);
+ }
+ }
+
+ if ($options['proxy']) {
+ $proxyUrl = parse_url($options['proxy']['url']);
+ $proxySocket = new SocketAddress($proxyUrl['host'], $proxyUrl['port']);
+ $proxyHeaders = $options['proxy']['auth'] ? ['Proxy-Authorization' => $options['proxy']['auth']] : [];
+
+ if ('ssl' === $proxyUrl['scheme']) {
+ $connector = new Https1TunnelConnector($proxySocket, $context->getTlsContext(), $proxyHeaders, $connector);
+ } else {
+ $connector = new Http1TunnelConnector($proxySocket, $proxyHeaders, $connector);
+ }
+ }
+
+ $maxHostConnections = 0 < $this->maxHostConnections ? $this->maxHostConnections : PHP_INT_MAX;
+ $pool = new DefaultConnectionFactory($connector, $context);
+ $pool = ConnectionLimitingPool::byAuthority($maxHostConnections, $pool);
+
+ return $this->clients[$key] = [($this->clientConfigurator)(new PooledHttpClient($pool)), $handleConnector];
+ }
+
+ private function handlePush(Request $request, Promise $response, array $options): Promise
+ {
+ $deferred = new Deferred();
+ $authority = $request->getUri()->getAuthority();
+
+ if ($this->maxPendingPushes <= \count($this->pushedResponses[$authority] ?? [])) {
+ $fifoUrl = key($this->pushedResponses[$authority]);
+ unset($this->pushedResponses[$authority][$fifoUrl]);
+ $this->logger && $this->logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl));
+ }
+
+ $url = (string) $request->getUri();
+ $this->logger && $this->logger->debug(sprintf('Queueing pushed response: "%s"', $url));
+ $this->pushedResponses[$authority][] = [$url, $deferred, $request, $response, [
+ 'proxy' => $options['proxy'],
+ 'bindto' => $options['bindto'],
+ 'local_cert' => $options['local_cert'],
+ 'local_pk' => $options['local_pk'],
+ ]];
+
+ return $deferred->promise();
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Internal/AmpListener.php b/src/Symfony/Component/HttpClient/Internal/AmpListener.php
new file mode 100644
index 0000000000000..cb3235bca3ff6
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Internal/AmpListener.php
@@ -0,0 +1,183 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Internal;
+
+use Amp\Http\Client\Connection\Stream;
+use Amp\Http\Client\EventListener;
+use Amp\Http\Client\Request;
+use Amp\Promise;
+use Amp\Success;
+use Symfony\Component\HttpClient\Exception\TransportException;
+
+/**
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+class AmpListener implements EventListener
+{
+ private $info;
+ private $pinSha256;
+ private $onProgress;
+ private $handle;
+
+ public function __construct(array &$info, array $pinSha256, \Closure $onProgress, &$handle)
+ {
+ $info += [
+ 'connect_time' => 0.0,
+ 'pretransfer_time' => 0.0,
+ 'starttransfer_time' => 0.0,
+ 'total_time' => 0.0,
+ 'namelookup_time' => 0.0,
+ 'primary_ip' => '',
+ 'primary_port' => 0,
+ ];
+
+ $this->info = &$info;
+ $this->pinSha256 = $pinSha256;
+ $this->onProgress = $onProgress;
+ $this->handle = &$handle;
+ }
+
+ public function startRequest(Request $request): Promise
+ {
+ $this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function startDnsResolution(Request $request): Promise
+ {
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function startConnectionCreation(Request $request): Promise
+ {
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function startTlsNegotiation(Request $request): Promise
+ {
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function startSendingRequest(Request $request, Stream $stream): Promise
+ {
+ $host = $stream->getRemoteAddress()->getHost();
+
+ if (false !== strpos($host, ':')) {
+ $host = '['.$host.']';
+ }
+
+ $this->info['primary_ip'] = $host;
+ $this->info['primary_port'] = $stream->getRemoteAddress()->getPort();
+ $this->info['pretransfer_time'] = microtime(true) - $this->info['start_time'];
+ $this->info['debug'] .= sprintf("* Connected to %s (%s) port %d\n", $request->getUri()->getHost(), $host, $this->info['primary_port']);
+
+ if ((isset($this->info['peer_certificate_chain']) || $this->pinSha256) && null !== $tlsInfo = $stream->getTlsInfo()) {
+ foreach ($tlsInfo->getPeerCertificates() as $cert) {
+ $this->info['peer_certificate_chain'][] = openssl_x509_read($cert->toPem());
+ }
+
+ if ($this->pinSha256) {
+ $pin = openssl_pkey_get_public($this->info['peer_certificate_chain'][0]);
+ $pin = openssl_pkey_get_details($pin)['key'];
+ $pin = \array_slice(explode("\n", $pin), 1, -2);
+ $pin = base64_decode(implode('', $pin));
+ $pin = base64_encode(hash('sha256', $pin, true));
+
+ if (!\in_array($pin, $this->pinSha256, true)) {
+ throw new TransportException(sprintf('SSL public key does not match pinned public key for "%s".', $this->info['url']));
+ }
+ }
+ }
+ ($this->onProgress)();
+
+ $uri = $request->getUri();
+ $requestUri = $uri->getPath() ?: '/';
+
+ if ('' !== $query = $uri->getQuery()) {
+ $requestUri .= '?'.$query;
+ }
+
+ if ('CONNECT' === $method = $request->getMethod()) {
+ $requestUri = $uri->getHost().': '.($uri->getPort() ?? ('https' === $uri->getScheme() ? 443 : 80));
+ }
+
+ $this->info['debug'] .= sprintf("> %s %s HTTP/%s \r\n", $method, $requestUri, $request->getProtocolVersions()[0]);
+
+ foreach ($request->getRawHeaders() as [$name, $value]) {
+ $this->info['debug'] .= $name.': '.$value."\r\n";
+ }
+ $this->info['debug'] .= "\r\n";
+
+ return new Success();
+ }
+
+ public function completeSendingRequest(Request $request, Stream $stream): Promise
+ {
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function startReceivingResponse(Request $request, Stream $stream): Promise
+ {
+ $this->info['starttransfer_time'] = microtime(true) - $this->info['start_time'];
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function completeReceivingResponse(Request $request, Stream $stream): Promise
+ {
+ $this->handle = null;
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function completeDnsResolution(Request $request): Promise
+ {
+ $this->info['namelookup_time'] = microtime(true) - $this->info['start_time'];
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function completeConnectionCreation(Request $request): Promise
+ {
+ $this->info['connect_time'] = microtime(true) - $this->info['start_time'];
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function completeTlsNegotiation(Request $request): Promise
+ {
+ ($this->onProgress)();
+
+ return new Success();
+ }
+
+ public function abort(Request $request, \Throwable $cause): Promise
+ {
+ return new Success();
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Internal/AmpResolver.php b/src/Symfony/Component/HttpClient/Internal/AmpResolver.php
new file mode 100644
index 0000000000000..d31476a5832b1
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Internal/AmpResolver.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Internal;
+
+use Amp\Dns;
+use Amp\Dns\Record;
+use Amp\Promise;
+use Amp\Success;
+
+/**
+ * Handles local overrides for the DNS resolver.
+ *
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+class AmpResolver implements Dns\Resolver
+{
+ private $dnsMap;
+
+ public function __construct(array &$dnsMap)
+ {
+ $this->dnsMap = &$dnsMap;
+ }
+
+ public function resolve(string $name, int $typeRestriction = null): Promise
+ {
+ if (!isset($this->dnsMap[$name]) || !\in_array($typeRestriction, [Record::A, null], true)) {
+ return Dns\resolver()->resolve($name, $typeRestriction);
+ }
+
+ return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
+ }
+
+ public function query(string $name, int $type): Promise
+ {
+ if (!isset($this->dnsMap[$name]) || Record::A !== $type) {
+ return Dns\resolver()->query($name, $type);
+ }
+
+ return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php b/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php
index 3f287feb6b80d..f17a4a78503c3 100644
--- a/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php
+++ b/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php
@@ -72,7 +72,7 @@ public function wait(?ResponseInterface $pendingResponse, float $maxDuration = n
goto check_duration;
}
- if ([$request, $promise] = $this->promisePool[$response] ?? null) {
+ if ([, $promise] = $this->promisePool[$response] ?? null) {
unset($this->promisePool[$response]);
$promise->resolve($this->createPsr7Response($response, true));
}
diff --git a/src/Symfony/Component/HttpClient/MockHttpClient.php b/src/Symfony/Component/HttpClient/MockHttpClient.php
index cb3cb969b06ba..d69276ffb2377 100644
--- a/src/Symfony/Component/HttpClient/MockHttpClient.php
+++ b/src/Symfony/Component/HttpClient/MockHttpClient.php
@@ -29,9 +29,10 @@ class MockHttpClient implements HttpClientInterface
private $responseFactory;
private $baseUri;
+ private $requestsCount = 0;
/**
- * @param callable|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
+ * @param callable|callable[]|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
*/
public function __construct($responseFactory = null, string $baseUri = null)
{
@@ -64,9 +65,11 @@ public function request(string $method, string $url, array $options = []): Respo
} elseif (!$this->responseFactory->valid()) {
throw new TransportException('The response factory iterator passed to MockHttpClient is empty.');
} else {
- $response = $this->responseFactory->current();
+ $responseFactory = $this->responseFactory->current();
+ $response = \is_callable($responseFactory) ? $responseFactory($method, $url, $options) : $responseFactory;
$this->responseFactory->next();
}
+ ++$this->requestsCount;
return MockResponse::fromRequest($method, $url, $options, $response);
}
@@ -84,4 +87,9 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa
return new ResponseStream(MockResponse::stream($responses, $timeout));
}
+
+ public function getRequestsCount(): int
+ {
+ return $this->requestsCount;
+ }
}
diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php
index 2cdb2029220b5..60bced566d00c 100644
--- a/src/Symfony/Component/HttpClient/NativeHttpClient.php
+++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php
@@ -219,13 +219,10 @@ public function request(string $method, string $url, array $options = []): Respo
],
];
- $proxy = self::getProxy($options['proxy'], $url);
- $noProxy = $options['no_proxy'] ?? $_SERVER['no_proxy'] ?? $_SERVER['NO_PROXY'] ?? '';
- $noProxy = $noProxy ? preg_split('/[\s,]+/', $noProxy) : [];
-
- $resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $noProxy, $info, $onProgress);
+ $proxy = self::getProxy($options['proxy'], $url, $options['no_proxy']);
+ $resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $info, $onProgress);
$context = stream_context_create($context, ['notification' => $notification]);
- self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, $noProxy);
+ self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy);
return new NativeResponse($this->multi, $context, implode('', $url), $options, $info, $resolveRedirect, $onProgress, $this->logger);
}
@@ -267,44 +264,6 @@ private static function getBodyAsString($body): string
return $result;
}
- /**
- * Loads proxy configuration from the same environment variables as curl when no proxy is explicitly set.
- */
- private static function getProxy(?string $proxy, array $url): ?array
- {
- if (null === $proxy) {
- // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities
- $proxy = $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null;
-
- if ('https:' === $url['scheme']) {
- $proxy = $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? $proxy;
- }
- }
-
- if (null === $proxy) {
- return null;
- }
-
- $proxy = (parse_url($proxy) ?: []) + ['scheme' => 'http'];
-
- if (!isset($proxy['host'])) {
- throw new TransportException('Invalid HTTP proxy: host is missing.');
- }
-
- if ('http' === $proxy['scheme']) {
- $proxyUrl = 'tcp://'.$proxy['host'].':'.($proxy['port'] ?? '80');
- } elseif ('https' === $proxy['scheme']) {
- $proxyUrl = 'ssl://'.$proxy['host'].':'.($proxy['port'] ?? '443');
- } else {
- throw new TransportException(sprintf('Unsupported proxy scheme "%s": "http" or "https" expected.', $proxy['scheme']));
- }
-
- return [
- 'url' => $proxyUrl,
- 'auth' => isset($proxy['user']) ? 'Basic '.base64_encode(rawurldecode($proxy['user']).':'.rawurldecode($proxy['pass'] ?? '')) : null,
- ];
- }
-
/**
* Resolves the IP of the host using the local DNS cache if possible.
*/
@@ -347,7 +306,7 @@ private static function dnsResolve(array $url, NativeClientState $multi, array &
/**
* Handles redirects - the native logic is too buggy to be used.
*/
- private static function createRedirectResolver(array $options, string $host, ?array $proxy, array $noProxy, array &$info, ?\Closure $onProgress): \Closure
+ private static function createRedirectResolver(array $options, string $host, ?array $proxy, array &$info, ?\Closure $onProgress): \Closure
{
$redirectHeaders = [];
if (0 < $maxRedirects = $options['max_redirects']) {
@@ -363,7 +322,7 @@ private static function createRedirectResolver(array $options, string $host, ?ar
}
}
- return static function (NativeClientState $multi, ?string $location, $context) use ($redirectHeaders, $proxy, $noProxy, &$info, $maxRedirects, $onProgress): ?string {
+ return static function (NativeClientState $multi, ?string $location, $context) use ($redirectHeaders, $proxy, &$info, $maxRedirects, $onProgress): ?string {
if (null === $location || $info['http_code'] < 300 || 400 <= $info['http_code']) {
$info['redirect_url'] = null;
@@ -411,14 +370,14 @@ private static function createRedirectResolver(array $options, string $host, ?ar
// Authorization and Cookie headers MUST NOT follow except for the initial host name
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
$requestHeaders[] = 'Host: '.$host.$port;
- self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, $noProxy);
+ self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy);
}
return implode('', $url);
};
}
- private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy, array $noProxy)
+ private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy): bool
{
if (null === $proxy) {
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
@@ -426,7 +385,7 @@ private static function configureHeadersAndProxy($context, string $host, array $
// Matching "no_proxy" should follow the behavior of curl
- foreach ($noProxy as $rule) {
+ foreach ($proxy['no_proxy'] as $rule) {
$dotRule = '.'.ltrim($rule, '.');
if ('*' === $rule || $host === $rule || substr($host, -\strlen($dotRule)) === $dotRule) {
diff --git a/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php b/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php
new file mode 100644
index 0000000000000..c01b906a00303
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php
@@ -0,0 +1,113 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient;
+
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
+use Symfony\Component\HttpClient\Exception\TransportException;
+use Symfony\Component\HttpFoundation\IpUtils;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+use Symfony\Contracts\HttpClient\ResponseStreamInterface;
+
+/**
+ * Decorator that blocks requests to private networks by default.
+ *
+ * @author Hallison Boaventura
+ */
+final class NoPrivateNetworkHttpClient implements HttpClientInterface, LoggerAwareInterface
+{
+ use HttpClientTrait;
+
+ private const PRIVATE_SUBNETS = [
+ '127.0.0.0/8',
+ '10.0.0.0/8',
+ '192.168.0.0/16',
+ '172.16.0.0/12',
+ '169.254.0.0/16',
+ '0.0.0.0/8',
+ '240.0.0.0/4',
+ '::1/128',
+ 'fc00::/7',
+ 'fe80::/10',
+ '::ffff:0:0/96',
+ '::/128',
+ ];
+
+ private $client;
+ private $subnets;
+
+ /**
+ * @param string|array|null $subnets String or array of subnets using CIDR notation that will be used by IpUtils.
+ * If null is passed, the standard private subnets will be used.
+ */
+ public function __construct(HttpClientInterface $client, $subnets = null)
+ {
+ if (!(\is_array($subnets) || \is_string($subnets) || null === $subnets)) {
+ throw new \TypeError(sprintf('Argument 2 passed to %s() must be of the type array, string or null. %s given.', __METHOD__, \is_object($subnets) ? \get_class($subnets) : \gettype($subnets)));
+ }
+
+ if (!class_exists(IpUtils::class)) {
+ throw new \LogicException(sprintf('You can not use "%s" if the HttpFoundation component is not installed. Try running "composer require symfony/http-foundation".', __CLASS__));
+ }
+
+ $this->client = $client;
+ $this->subnets = $subnets;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function request(string $method, string $url, array $options = []): ResponseInterface
+ {
+ $onProgress = $options['on_progress'] ?? null;
+ if (null !== $onProgress && !\is_callable($onProgress)) {
+ throw new InvalidArgumentException(sprintf('Option "on_progress" must be callable, %s given.', \is_object($onProgress) ? \get_class($onProgress) : \gettype($onProgress)));
+ }
+
+ $subnets = $this->subnets;
+ $lastPrimaryIp = '';
+
+ $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastPrimaryIp): void {
+ if ($info['primary_ip'] !== $lastPrimaryIp) {
+ if (IpUtils::checkIp($info['primary_ip'], $subnets ?? self::PRIVATE_SUBNETS)) {
+ throw new TransportException(sprintf('IP "%s" is blacklisted for "%s".', $info['primary_ip'], $info['url']));
+ }
+
+ $lastPrimaryIp = $info['primary_ip'];
+ }
+
+ null !== $onProgress && $onProgress($dlNow, $dlSize, $info);
+ };
+
+ return $this->client->request($method, $url, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stream($responses, float $timeout = null): ResponseStreamInterface
+ {
+ return $this->client->stream($responses, $timeout);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLogger(LoggerInterface $logger): void
+ {
+ if ($this->client instanceof LoggerAwareInterface) {
+ $this->client->setLogger($logger);
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Response/AmpResponse.php b/src/Symfony/Component/HttpClient/Response/AmpResponse.php
new file mode 100644
index 0000000000000..e5d0bb213fc8a
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Response/AmpResponse.php
@@ -0,0 +1,400 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Response;
+
+use Amp\ByteStream\StreamException;
+use Amp\CancellationTokenSource;
+use Amp\Http\Client\HttpException;
+use Amp\Http\Client\Request;
+use Amp\Http\Client\Response;
+use Amp\Loop;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpClient\Chunk\FirstChunk;
+use Symfony\Component\HttpClient\Chunk\InformationalChunk;
+use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
+use Symfony\Component\HttpClient\Exception\TransportException;
+use Symfony\Component\HttpClient\HttpClientTrait;
+use Symfony\Component\HttpClient\Internal\AmpBody;
+use Symfony\Component\HttpClient\Internal\AmpClientState;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+
+/**
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+final class AmpResponse implements ResponseInterface
+{
+ use ResponseTrait;
+
+ private $multi;
+ private $options;
+ private $canceller;
+ private $onProgress;
+
+ /**
+ * @internal
+ */
+ public function __construct(AmpClientState $multi, Request $request, array $options, ?LoggerInterface $logger)
+ {
+ $this->multi = $multi;
+ $this->options = &$options;
+ $this->logger = $logger;
+ $this->timeout = $options['timeout'];
+ $this->shouldBuffer = $options['buffer'];
+
+ if ($this->inflate = \extension_loaded('zlib') && !$request->hasHeader('accept-encoding')) {
+ $request->setHeader('Accept-Encoding', 'gzip');
+ }
+
+ $this->initializer = static function (self $response) {
+ return null !== $response->options;
+ };
+
+ $info = &$this->info;
+ $headers = &$this->headers;
+ $canceller = $this->canceller = new CancellationTokenSource();
+ $handle = &$this->handle;
+
+ $info['url'] = (string) $request->getUri();
+ $info['http_method'] = $request->getMethod();
+ $info['start_time'] = null;
+ $info['redirect_url'] = null;
+ $info['redirect_time'] = 0.0;
+ $info['redirect_count'] = 0;
+ $info['size_upload'] = 0.0;
+ $info['size_download'] = 0.0;
+ $info['upload_content_length'] = -1.0;
+ $info['download_content_length'] = -1.0;
+ $info['user_data'] = $options['user_data'];
+ $info['debug'] = '';
+
+ $onProgress = $options['on_progress'] ?? static function () {};
+ $onProgress = $this->onProgress = static function () use (&$info, $onProgress) {
+ $info['total_time'] = microtime(true) - $info['start_time'];
+ $onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info);
+ };
+
+ $this->id = $id = Loop::defer(static function () use ($request, $multi, &$id, &$info, &$headers, $canceller, &$options, $onProgress, &$handle, $logger) {
+ return self::generateResponse($request, $multi, $id, $info, $headers, $canceller, $options, $onProgress, $handle, $logger);
+ });
+
+ $multi->openHandles[$id] = $id;
+ ++$multi->responseCount;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getInfo(string $type = null)
+ {
+ return null !== $type ? $this->info[$type] ?? null : $this->info;
+ }
+
+ public function __destruct()
+ {
+ try {
+ $this->doDestruct();
+ } finally {
+ $this->close();
+
+ // Clear the DNS cache when all requests completed
+ if (0 >= --$this->multi->responseCount) {
+ $this->multi->responseCount = 0;
+ $this->multi->dnsCache = [];
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ private function close(): void
+ {
+ $this->canceller->cancel();
+ unset($this->multi->openHandles[$this->id], $this->multi->handlesActivity[$this->id]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ private static function schedule(self $response, array &$runningResponses): void
+ {
+ if (isset($runningResponses[0])) {
+ $runningResponses[0][1][$response->id] = $response;
+ } else {
+ $runningResponses[0] = [$response->multi, [$response->id => $response]];
+ }
+
+ if (!isset($response->multi->openHandles[$response->id])) {
+ $response->multi->handlesActivity[$response->id][] = null;
+ $response->multi->handlesActivity[$response->id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ private static function perform(AmpClientState $multi, array &$responses = null): void
+ {
+ if ($responses) {
+ foreach ($responses as $response) {
+ try {
+ if ($response->info['start_time']) {
+ $response->info['total_time'] = microtime(true) - $response->info['start_time'];
+ ($response->onProgress)();
+ }
+ } catch (\Throwable $e) {
+ $multi->handlesActivity[$response->id][] = null;
+ $multi->handlesActivity[$response->id][] = $e;
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ private static function select(AmpClientState $multi, float $timeout): int
+ {
+ $selected = 1;
+ $delay = Loop::delay(1000 * $timeout, static function () use (&$selected) {
+ $selected = 0;
+ Loop::stop();
+ });
+ Loop::run();
+
+ if ($selected) {
+ Loop::cancel($delay);
+ }
+
+ return $selected;
+ }
+
+ private static function generateResponse(Request $request, AmpClientState $multi, string $id, array &$info, array &$headers, CancellationTokenSource $canceller, array &$options, \Closure $onProgress, &$handle, ?LoggerInterface $logger)
+ {
+ $activity = &$multi->handlesActivity;
+
+ $request->setInformationalResponseHandler(static function (Response $response) use (&$activity, $id, &$info, &$headers) {
+ self::addResponseHeaders($response, $info, $headers);
+ $activity[$id][] = new InformationalChunk($response->getStatus(), $response->getHeaders());
+ Loop::defer([Loop::class, 'stop']);
+ });
+
+ try {
+ /* @var Response $response */
+ if (null === $response = yield from self::getPushedResponse($request, $multi, $info, $headers, $options, $logger)) {
+ $logger && $logger->info(sprintf('Request: "%s %s"', $info['http_method'], $info['url']));
+
+ $response = yield from self::followRedirects($request, $multi, $info, $headers, $canceller, $options, $onProgress, $handle, $logger);
+ }
+
+ $options = null;
+
+ $activity[$id] = [new FirstChunk()];
+
+ if ('HEAD' === $response->getRequest()->getMethod() || \in_array($info['http_code'], [204, 304], true)) {
+ $activity[$id][] = null;
+ $activity[$id][] = null;
+ Loop::defer([Loop::class, 'stop']);
+
+ return;
+ }
+
+ if ($response->hasHeader('content-length')) {
+ $info['download_content_length'] = (float) $response->getHeader('content-length');
+ }
+
+ $body = $response->getBody();
+
+ while (true) {
+ Loop::defer([Loop::class, 'stop']);
+
+ if (null === $data = yield $body->read()) {
+ break;
+ }
+
+ $info['size_download'] += \strlen($data);
+ $activity[$id][] = $data;
+ }
+
+ $activity[$id][] = null;
+ $activity[$id][] = null;
+ } catch (\Throwable $e) {
+ $activity[$id][] = null;
+ $activity[$id][] = $e;
+ } finally {
+ $info['download_content_length'] = $info['size_download'];
+ }
+
+ Loop::defer([Loop::class, 'stop']);
+ }
+
+ private static function followRedirects(Request $originRequest, AmpClientState $multi, array &$info, array &$headers, CancellationTokenSource $canceller, array $options, \Closure $onProgress, &$handle, ?LoggerInterface $logger)
+ {
+ $originRequest->setBody(new AmpBody($options['body'], $info, $onProgress));
+ $response = yield $multi->request($options, $originRequest, $canceller->getToken(), $info, $onProgress, $handle);
+ $previousUrl = null;
+
+ while (true) {
+ self::addResponseHeaders($response, $info, $headers);
+ $status = $response->getStatus();
+
+ if (!\in_array($status, [301, 302, 303, 307, 308], true) || null === $location = $response->getHeader('location')) {
+ return $response;
+ }
+
+ $urlResolver = new class() {
+ use HttpClientTrait {
+ parseUrl as public;
+ resolveUrl as public;
+ }
+ };
+
+ try {
+ $previousUrl = $previousUrl ?? $urlResolver::parseUrl($info['url']);
+ $location = $urlResolver::parseUrl($location);
+ $location = $urlResolver::resolveUrl($location, $previousUrl);
+ $info['redirect_url'] = implode('', $location);
+ } catch (InvalidArgumentException $e) {
+ return $response;
+ }
+
+ if (0 >= $options['max_redirects'] || $info['redirect_count'] >= $options['max_redirects']) {
+ return $response;
+ }
+
+ $logger && $logger->info(sprintf('Redirecting: "%s %s"', $status, $info['url']));
+
+ try {
+ // Discard body of redirects
+ while (null !== yield $response->getBody()->read()) {
+ }
+ } catch (HttpException | StreamException $e) {
+ // Ignore streaming errors on previous responses
+ }
+
+ ++$info['redirect_count'];
+ $info['url'] = $info['redirect_url'];
+ $info['redirect_url'] = null;
+ $previousUrl = $location;
+
+ $request = new Request($info['url'], $info['http_method']);
+ $request->setProtocolVersions($originRequest->getProtocolVersions());
+ $request->setTcpConnectTimeout($originRequest->getTcpConnectTimeout());
+ $request->setTlsHandshakeTimeout($originRequest->getTlsHandshakeTimeout());
+ $request->setTransferTimeout($originRequest->getTransferTimeout());
+
+ if (\in_array($status, [301, 302, 303], true)) {
+ $originRequest->removeHeader('transfer-encoding');
+ $originRequest->removeHeader('content-length');
+ $originRequest->removeHeader('content-type');
+
+ // Do like curl and browsers: turn POST to GET on 301, 302 and 303
+ if ('POST' === $response->getRequest()->getMethod() || 303 === $status) {
+ $info['http_method'] = 'HEAD' === $response->getRequest()->getMethod() ? 'HEAD' : 'GET';
+ $request->setMethod($info['http_method']);
+ }
+ } else {
+ $request->setBody(AmpBody::rewind($response->getRequest()->getBody()));
+ }
+
+ foreach ($originRequest->getRawHeaders() as [$name, $value]) {
+ $request->setHeader($name, $value);
+ }
+
+ if ($request->getUri()->getAuthority() !== $originRequest->getUri()->getAuthority()) {
+ $request->removeHeader('authorization');
+ $request->removeHeader('cookie');
+ $request->removeHeader('host');
+ }
+
+ $response = yield $multi->request($options, $request, $canceller->getToken(), $info, $onProgress, $handle);
+ $info['redirect_time'] = microtime(true) - $info['start_time'];
+ }
+ }
+
+ private static function addResponseHeaders(Response $response, array &$info, array &$headers): void
+ {
+ $info['http_code'] = $response->getStatus();
+
+ if ($headers) {
+ $info['debug'] .= "< \r\n";
+ $headers = [];
+ }
+
+ $h = sprintf('HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatus(), $response->getReason());
+ $info['debug'] .= "< {$h}\r\n";
+ $info['response_headers'][] = $h;
+
+ foreach ($response->getRawHeaders() as [$name, $value]) {
+ $headers[strtolower($name)][] = $value;
+ $h = $name.': '.$value;
+ $info['debug'] .= "< {$h}\r\n";
+ $info['response_headers'][] = $h;
+ }
+
+ $info['debug'] .= "< \r\n";
+ }
+
+ /**
+ * Accepts pushed responses only if their headers related to authentication match the request.
+ */
+ private static function getPushedResponse(Request $request, AmpClientState $multi, array &$info, array &$headers, array $options, ?LoggerInterface $logger)
+ {
+ if ('' !== $options['body']) {
+ return null;
+ }
+
+ $authority = $request->getUri()->getAuthority();
+
+ foreach ($multi->pushedResponses[$authority] ?? [] as $i => [$pushedUrl, $pushDeferred, $pushedRequest, $pushedResponse, $parentOptions]) {
+ if ($info['url'] !== $pushedUrl || $info['http_method'] !== $pushedRequest->getMethod()) {
+ continue;
+ }
+
+ foreach ($parentOptions as $k => $v) {
+ if ($options[$k] !== $v) {
+ continue 2;
+ }
+ }
+
+ foreach (['authorization', 'cookie', 'range', 'proxy-authorization'] as $k) {
+ if ($pushedRequest->getHeaderArray($k) !== $request->getHeaderArray($k)) {
+ continue 2;
+ }
+ }
+
+ $response = yield $pushedResponse;
+
+ foreach ($response->getHeaderArray('vary') as $vary) {
+ foreach (preg_split('/\s*+,\s*+/', $vary) as $v) {
+ if ('*' === $v || ($pushedRequest->getHeaderArray($v) !== $request->getHeaderArray($v) && 'accept-encoding' !== strtolower($v))) {
+ $logger && $logger->debug(sprintf('Skipping pushed response: "%s"', $info['url']));
+ continue 3;
+ }
+ }
+ }
+
+ $pushDeferred->resolve();
+ $logger && $logger->debug(sprintf('Accepting pushed response: "%s %s"', $info['http_method'], $info['url']));
+ self::addResponseHeaders($response, $info, $headers);
+ unset($multi->pushedResponses[$authority][$i]);
+
+ if (!$multi->pushedResponses[$authority]) {
+ unset($multi->pushedResponses[$authority]);
+ }
+
+ return $response;
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
index 105d11671a7b2..0755c2287648c 100644
--- a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
+++ b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
@@ -49,6 +49,14 @@ class StreamWrapper
*/
public static function createResource(ResponseInterface $response, HttpClientInterface $client = null)
{
+ if (\is_callable([$response, 'toStream']) && isset(class_uses($response)[ResponseTrait::class])) {
+ $stack = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+ if ($response !== ($stack[1]['object'] ?? null)) {
+ return $response->toStream(false);
+ }
+ }
+
if (null === $client && !method_exists($response, 'stream')) {
throw new \InvalidArgumentException(sprintf('Providing a client to "%s()" is required when the response doesn\'t have any "stream()" method.', __CLASS__));
}
diff --git a/src/Symfony/Component/HttpClient/Response/TraceableResponse.php b/src/Symfony/Component/HttpClient/Response/TraceableResponse.php
new file mode 100644
index 0000000000000..9305e9be942d0
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Response/TraceableResponse.php
@@ -0,0 +1,122 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Response;
+
+use Symfony\Component\HttpClient\Exception\ClientException;
+use Symfony\Component\HttpClient\Exception\RedirectionException;
+use Symfony\Component\HttpClient\Exception\ServerException;
+use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+
+/**
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+class TraceableResponse implements ResponseInterface
+{
+ private $client;
+ private $response;
+ private $content;
+
+ public function __construct(HttpClientInterface $client, ResponseInterface $response, &$content)
+ {
+ $this->client = $client;
+ $this->response = $response;
+ $this->content = &$content;
+ }
+
+ public function getStatusCode(): int
+ {
+ return $this->response->getStatusCode();
+ }
+
+ public function getHeaders(bool $throw = true): array
+ {
+ return $this->response->getHeaders($throw);
+ }
+
+ public function getContent(bool $throw = true): string
+ {
+ $this->content = $this->response->getContent(false);
+
+ if ($throw) {
+ $this->checkStatusCode($this->response->getStatusCode());
+ }
+
+ return $this->content;
+ }
+
+ public function toArray(bool $throw = true): array
+ {
+ $this->content = $this->response->toArray(false);
+
+ if ($throw) {
+ $this->checkStatusCode($this->response->getStatusCode());
+ }
+
+ return $this->content;
+ }
+
+ public function cancel(): void
+ {
+ $this->response->cancel();
+ }
+
+ public function getInfo(string $type = null)
+ {
+ return $this->response->getInfo($type);
+ }
+
+ /**
+ * Casts the response to a PHP stream resource.
+ *
+ * @return resource
+ *
+ * @throws TransportExceptionInterface When a network error occurs
+ * @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
+ * @throws ClientExceptionInterface On a 4xx when $throw is true
+ * @throws ServerExceptionInterface On a 5xx when $throw is true
+ */
+ public function toStream(bool $throw = true)
+ {
+ if ($throw) {
+ // Ensure headers arrived
+ $this->response->getHeaders(true);
+ }
+
+ if (\is_callable([$this->response, 'toStream'])) {
+ return $this->response->toStream(false);
+ }
+
+ return StreamWrapper::createResource($this->response, $this->client);
+ }
+
+ private function checkStatusCode($code)
+ {
+ if (500 <= $code) {
+ throw new ServerException($this);
+ }
+
+ if (400 <= $code) {
+ throw new ClientException($this);
+ }
+
+ if (300 <= $code) {
+ throw new RedirectionException($this);
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/ScopingHttpClient.php b/src/Symfony/Component/HttpClient/ScopingHttpClient.php
index a55d011953086..66dcccf0e93f2 100644
--- a/src/Symfony/Component/HttpClient/ScopingHttpClient.php
+++ b/src/Symfony/Component/HttpClient/ScopingHttpClient.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpClient;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -22,7 +24,7 @@
*
* @author Anthony Martin
*/
-class ScopingHttpClient implements HttpClientInterface, ResetInterface
+class ScopingHttpClient implements HttpClientInterface, ResetInterface, LoggerAwareInterface
{
use HttpClientTrait;
@@ -98,4 +100,14 @@ public function reset()
$this->client->reset();
}
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLogger(LoggerInterface $logger): void
+ {
+ if ($this->client instanceof LoggerAwareInterface) {
+ $this->client->setLogger($logger);
+ }
+ }
}
diff --git a/src/Symfony/Component/HttpClient/Tests/AmpHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/AmpHttpClientTest.php
new file mode 100644
index 0000000000000..e17b45a0ce185
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Tests/AmpHttpClientTest.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Tests;
+
+use Symfony\Component\HttpClient\AmpHttpClient;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+
+class AmpHttpClientTest extends HttpClientTestCase
+{
+ protected function getHttpClient(string $testCase): HttpClientInterface
+ {
+ return new AmpHttpClient(['verify_peer' => false, 'verify_host' => false, 'timeout' => 5]);
+ }
+
+ public function testProxy()
+ {
+ $this->markTestSkipped('A real proxy server would be needed.');
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
index f83edf91b6f39..d238034f451e2 100644
--- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
@@ -11,155 +11,26 @@
namespace Symfony\Component\HttpClient\Tests;
-use Psr\Log\AbstractLogger;
use Symfony\Component\HttpClient\CurlHttpClient;
-use Symfony\Component\Process\Exception\ProcessFailedException;
-use Symfony\Component\Process\Process;
use Symfony\Contracts\HttpClient\HttpClientInterface;
-/*
-Tests for HTTP2 Push need a recent version of both PHP and curl. This docker command should run them:
-docker run -it --rm -v $(pwd):/app -v /path/to/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push
-The vulcain binary can be found at https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz - see https://github.com/dunglas/vulcain for source
-*/
-
/**
* @requires extension curl
*/
class CurlHttpClientTest extends HttpClientTestCase
{
- private static $vulcainStarted = false;
-
protected function getHttpClient(string $testCase): HttpClientInterface
{
- return new CurlHttpClient();
- }
-
- /**
- * @requires PHP 7.2.17
- */
- public function testHttp2PushVulcain()
- {
- $client = $this->getVulcainClient();
- $logger = new TestLogger();
- $client->setLogger($logger);
-
- $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
- 'headers' => [
- 'Preload' => '/documents/*/id',
- ],
- ])->toArray();
-
- foreach ($responseAsArray['documents'] as $document) {
- $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray();
- }
-
- $client->reset();
-
- $expected = [
- 'Request: "GET https://127.0.0.1:3000/json"',
- 'Queueing pushed response: "https://127.0.0.1:3000/json/1"',
- 'Queueing pushed response: "https://127.0.0.1:3000/json/2"',
- 'Queueing pushed response: "https://127.0.0.1:3000/json/3"',
- 'Response: "200 https://127.0.0.1:3000/json"',
- 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"',
- 'Response: "200 https://127.0.0.1:3000/json/1"',
- 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"',
- 'Response: "200 https://127.0.0.1:3000/json/2"',
- 'Accepting pushed response: "GET https://127.0.0.1:3000/json/3"',
- 'Response: "200 https://127.0.0.1:3000/json/3"',
- ];
- $this->assertSame($expected, $logger->logs);
- }
-
- /**
- * @requires PHP 7.2.17
- */
- public function testHttp2PushVulcainWithUnusedResponse()
- {
- $client = $this->getVulcainClient();
- $logger = new TestLogger();
- $client->setLogger($logger);
-
- $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
- 'headers' => [
- 'Preload' => '/documents/*/id',
- ],
- ])->toArray();
-
- $i = 0;
- foreach ($responseAsArray['documents'] as $document) {
- $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray();
- if (++$i >= 2) {
- break;
+ if (false !== strpos($testCase, 'Push')) {
+ if (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304) {
+ $this->markTestSkipped('PHP 7.3.0 to 7.3.3 don\'t support HTTP/2 PUSH');
}
- }
-
- $client->reset();
-
- $expected = [
- 'Request: "GET https://127.0.0.1:3000/json"',
- 'Queueing pushed response: "https://127.0.0.1:3000/json/1"',
- 'Queueing pushed response: "https://127.0.0.1:3000/json/2"',
- 'Queueing pushed response: "https://127.0.0.1:3000/json/3"',
- 'Response: "200 https://127.0.0.1:3000/json"',
- 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"',
- 'Response: "200 https://127.0.0.1:3000/json/1"',
- 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"',
- 'Response: "200 https://127.0.0.1:3000/json/2"',
- 'Unused pushed response: "https://127.0.0.1:3000/json/3"',
- ];
- $this->assertSame($expected, $logger->logs);
- }
- private function getVulcainClient(): CurlHttpClient
- {
- if (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304) {
- $this->markTestSkipped('PHP 7.3.0 to 7.3.3 don\'t support HTTP/2 PUSH');
- }
-
- if (!\defined('CURLMOPT_PUSHFUNCTION') || 0x073d00 > ($v = curl_version())['version_number'] || !(CURL_VERSION_HTTP2 & $v['features'])) {
- $this->markTestSkipped('curl <7.61 is used or it is not compiled with support for HTTP/2 PUSH');
- }
-
- $client = new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]);
-
- if (static::$vulcainStarted) {
- return $client;
- }
-
- if (['application/json'] !== $client->request('GET', 'http://127.0.0.1:8057/json')->getHeaders()['content-type']) {
- $this->markTestSkipped('symfony/http-client-contracts >= 2.0.1 required');
- }
-
- $process = new Process(['vulcain'], null, [
- 'DEBUG' => 1,
- 'UPSTREAM' => 'http://127.0.0.1:8057',
- 'ADDR' => ':3000',
- 'KEY_FILE' => __DIR__.'/Fixtures/tls/server.key',
- 'CERT_FILE' => __DIR__.'/Fixtures/tls/server.crt',
- ]);
- $process->start();
-
- register_shutdown_function([$process, 'stop']);
- sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1);
-
- if (!$process->isRunning()) {
- throw new ProcessFailedException($process);
+ if (!\defined('CURLMOPT_PUSHFUNCTION') || 0x073d00 > ($v = curl_version())['version_number'] || !(CURL_VERSION_HTTP2 & $v['features'])) {
+ $this->markTestSkipped('curl <7.61 is used or it is not compiled with support for HTTP/2 PUSH');
+ }
}
- static::$vulcainStarted = true;
-
- return $client;
- }
-}
-
-class TestLogger extends AbstractLogger
-{
- public $logs = [];
-
- public function log($level, $message, array $context = []): void
- {
- $this->logs[] = $message;
+ return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]);
}
}
diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php
index 9f70b743965d5..e2b0d9f6ebef3 100644
--- a/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTest.php
@@ -20,7 +20,7 @@ class HttpClientTest extends TestCase
{
public function testCreateClient()
{
- if (\extension_loaded('curl')) {
+ if (\extension_loaded('curl') && ('\\' !== \DIRECTORY_SEPARATOR || ini_get('curl.cainfo') || ini_get('openssl.cafile') || ini_get('openssl.capath'))) {
$this->assertInstanceOf(CurlHttpClient::class, HttpClient::create());
} else {
$this->assertInstanceOf(NativeHttpClient::class, HttpClient::create());
diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
index 29171969b457e..c9c667c11d1a6 100644
--- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
+++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
@@ -12,10 +12,22 @@
namespace Symfony\Component\HttpClient\Tests;
use Symfony\Component\HttpClient\Exception\ClientException;
+use Symfony\Component\HttpClient\Response\StreamWrapper;
+use Symfony\Component\Process\Exception\ProcessFailedException;
+use Symfony\Component\Process\Process;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\Test\HttpClientTestCase as BaseHttpClientTestCase;
+/*
+Tests for HTTP2 Push need a recent version of both PHP and curl. This docker command should run them:
+docker run -it --rm -v $(pwd):/app -v /path/to/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient --filter Push
+The vulcain binary can be found at https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz - see https://github.com/dunglas/vulcain for source
+*/
+
abstract class HttpClientTestCase extends BaseHttpClientTestCase
{
+ private static $vulcainStarted = false;
+
public function testAcceptHeader()
{
$client = $this->getHttpClient(__FUNCTION__);
@@ -91,4 +103,146 @@ public function testNonBlockingStream()
$this->assertSame('', fread($stream, 8192));
$this->assertTrue(feof($stream));
}
+
+ public function testResponseStreamRewind()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ $response = $client->request('GET', 'http://localhost:8057/103');
+
+ $stream = $response->toStream();
+
+ $this->assertSame('Here the body', stream_get_contents($stream));
+ rewind($stream);
+ $this->assertSame('Here the body', stream_get_contents($stream));
+ }
+
+ public function testStreamWrapperStreamRewind()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ $response = $client->request('GET', 'http://localhost:8057/103');
+
+ $stream = StreamWrapper::createResource($response);
+
+ $this->assertSame('Here the body', stream_get_contents($stream));
+ rewind($stream);
+ $this->assertSame('Here the body', stream_get_contents($stream));
+ }
+
+ public function testStreamWrapperWithClientStreamRewind()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ $response = $client->request('GET', 'http://localhost:8057/103');
+
+ $stream = StreamWrapper::createResource($response, $client);
+
+ $this->assertSame('Here the body', stream_get_contents($stream));
+ rewind($stream);
+ $this->assertSame('Here the body', stream_get_contents($stream));
+ }
+
+ public function testHttp2PushVulcain()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ self::startVulcain($client);
+ $logger = new TestLogger();
+ $client->setLogger($logger);
+
+ $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
+ 'headers' => [
+ 'Preload' => '/documents/*/id',
+ ],
+ ])->toArray();
+
+ foreach ($responseAsArray['documents'] as $document) {
+ $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray();
+ }
+
+ $client->reset();
+
+ $expected = [
+ 'Request: "GET https://127.0.0.1:3000/json"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/1"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/2"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/3"',
+ 'Response: "200 https://127.0.0.1:3000/json"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"',
+ 'Response: "200 https://127.0.0.1:3000/json/1"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"',
+ 'Response: "200 https://127.0.0.1:3000/json/2"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/3"',
+ 'Response: "200 https://127.0.0.1:3000/json/3"',
+ ];
+ $this->assertSame($expected, $logger->logs);
+ }
+
+ public function testHttp2PushVulcainWithUnusedResponse()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ self::startVulcain($client);
+ $logger = new TestLogger();
+ $client->setLogger($logger);
+
+ $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
+ 'headers' => [
+ 'Preload' => '/documents/*/id',
+ ],
+ ])->toArray();
+
+ $i = 0;
+ foreach ($responseAsArray['documents'] as $document) {
+ $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray();
+ if (++$i >= 2) {
+ break;
+ }
+ }
+
+ $client->reset();
+
+ $expected = [
+ 'Request: "GET https://127.0.0.1:3000/json"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/1"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/2"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/3"',
+ 'Response: "200 https://127.0.0.1:3000/json"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"',
+ 'Response: "200 https://127.0.0.1:3000/json/1"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"',
+ 'Response: "200 https://127.0.0.1:3000/json/2"',
+ 'Unused pushed response: "https://127.0.0.1:3000/json/3"',
+ ];
+ $this->assertSame($expected, $logger->logs);
+ }
+
+ private static function startVulcain(HttpClientInterface $client)
+ {
+ if (self::$vulcainStarted) {
+ return;
+ }
+
+ if ('\\' === \DIRECTORY_SEPARATOR) {
+ self::markTestSkipped('Testing with the "vulcain" is not supported on Windows.');
+ }
+
+ if (['application/json'] !== $client->request('GET', 'http://127.0.0.1:8057/json')->getHeaders()['content-type']) {
+ self::markTestSkipped('symfony/http-client-contracts >= 2.0.1 required');
+ }
+
+ $process = new Process(['vulcain'], null, [
+ 'DEBUG' => 1,
+ 'UPSTREAM' => 'http://127.0.0.1:8057',
+ 'ADDR' => ':3000',
+ 'KEY_FILE' => __DIR__.'/Fixtures/tls/server.key',
+ 'CERT_FILE' => __DIR__.'/Fixtures/tls/server.crt',
+ ]);
+ $process->start();
+
+ register_shutdown_function([$process, 'stop']);
+ sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1);
+
+ if (!$process->isRunning()) {
+ throw new ProcessFailedException($process);
+ }
+
+ self::$vulcainStarted = true;
+ }
}
diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
index bce4bfafea8cc..966971823e8c6 100644
--- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
@@ -22,6 +22,127 @@
class MockHttpClientTest extends HttpClientTestCase
{
+ /**
+ * @dataProvider mockingProvider
+ */
+ public function testMocking($factory, array $expectedResponses)
+ {
+ $client = new MockHttpClient($factory, 'https://example.com/');
+ $this->assertSame(0, $client->getRequestsCount());
+
+ $urls = ['/foo', '/bar'];
+ foreach ($urls as $i => $url) {
+ $response = $client->request('POST', $url, ['body' => 'payload']);
+ $this->assertEquals($expectedResponses[$i], $response->getContent());
+ }
+
+ $this->assertSame(2, $client->getRequestsCount());
+ }
+
+ public function mockingProvider(): iterable
+ {
+ yield 'callable' => [
+ static function (string $method, string $url, array $options = []) {
+ return new MockResponse($method.': '.$url.' (body='.$options['body'].')');
+ },
+ [
+ 'POST: https://example.com/foo (body=payload)',
+ 'POST: https://example.com/bar (body=payload)',
+ ],
+ ];
+
+ yield 'array of callable' => [
+ [
+ static function (string $method, string $url, array $options = []) {
+ return new MockResponse($method.': '.$url.' (body='.$options['body'].') [1]');
+ },
+ static function (string $method, string $url, array $options = []) {
+ return new MockResponse($method.': '.$url.' (body='.$options['body'].') [2]');
+ },
+ ],
+ [
+ 'POST: https://example.com/foo (body=payload) [1]',
+ 'POST: https://example.com/bar (body=payload) [2]',
+ ],
+ ];
+
+ yield 'array of response objects' => [
+ [
+ new MockResponse('static response [1]'),
+ new MockResponse('static response [2]'),
+ ],
+ [
+ 'static response [1]',
+ 'static response [2]',
+ ],
+ ];
+
+ yield 'iterator' => [
+ new \ArrayIterator(
+ [
+ new MockResponse('static response [1]'),
+ new MockResponse('static response [2]'),
+ ]
+ ),
+ [
+ 'static response [1]',
+ 'static response [2]',
+ ],
+ ];
+
+ yield 'null' => [
+ null,
+ [
+ '',
+ '',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider transportExceptionProvider
+ */
+ public function testTransportExceptionThrowsIfPerformedMoreRequestsThanConfigured($factory)
+ {
+ $client = new MockHttpClient($factory, 'https://example.com/');
+
+ $client->request('POST', '/foo');
+ $client->request('POST', '/foo');
+
+ $this->expectException(TransportException::class);
+ $client->request('POST', '/foo');
+ }
+
+ public function transportExceptionProvider(): iterable
+ {
+ yield 'array of callable' => [
+ [
+ static function (string $method, string $url, array $options = []) {
+ return new MockResponse();
+ },
+ static function (string $method, string $url, array $options = []) {
+ return new MockResponse();
+ },
+ ],
+ ];
+
+ yield 'array of response objects' => [
+ [
+ new MockResponse(),
+ new MockResponse(),
+ ],
+ ];
+
+ yield 'iterator' => [
+ new \ArrayIterator(
+ [
+ new MockResponse(),
+ new MockResponse(),
+ ]
+ ),
+ ];
+ }
+
protected function getHttpClient(string $testCase): HttpClientInterface
{
$responses = [];
@@ -191,4 +312,14 @@ protected function getHttpClient(string $testCase): HttpClientInterface
return new MockHttpClient($responses);
}
+
+ public function testHttp2PushVulcain()
+ {
+ $this->markTestSkipped('MockHttpClient doesn\'t support HTTP/2 PUSH.');
+ }
+
+ public function testHttp2PushVulcainWithUnusedResponse()
+ {
+ $this->markTestSkipped('MockHttpClient doesn\'t support HTTP/2 PUSH.');
+ }
}
diff --git a/src/Symfony/Component/HttpClient/Tests/NativeHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/NativeHttpClientTest.php
index bcfab64bdcace..819124f1745b4 100644
--- a/src/Symfony/Component/HttpClient/Tests/NativeHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/NativeHttpClientTest.php
@@ -25,4 +25,14 @@ public function testInformationalResponseStream()
{
$this->markTestSkipped('NativeHttpClient doesn\'t support informational status codes.');
}
+
+ public function testHttp2PushVulcain()
+ {
+ $this->markTestSkipped('NativeHttpClient doesn\'t support HTTP/2.');
+ }
+
+ public function testHttp2PushVulcainWithUnusedResponse()
+ {
+ $this->markTestSkipped('NativeHttpClient doesn\'t support HTTP/2.');
+ }
}
diff --git a/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php
new file mode 100755
index 0000000000000..926dead34f6e5
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php
@@ -0,0 +1,164 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
+use Symfony\Component\HttpClient\Exception\TransportException;
+use Symfony\Component\HttpClient\MockHttpClient;
+use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
+use Symfony\Component\HttpClient\Response\MockResponse;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+
+class NoPrivateNetworkHttpClientTest extends TestCase
+{
+ public function getBlacklistData(): array
+ {
+ return [
+ // private
+ ['0.0.0.1', null, true],
+ ['169.254.0.1', null, true],
+ ['127.0.0.1', null, true],
+ ['240.0.0.1', null, true],
+ ['10.0.0.1', null, true],
+ ['172.16.0.1', null, true],
+ ['192.168.0.1', null, true],
+ ['::1', null, true],
+ ['::ffff:0:1', null, true],
+ ['fe80::1', null, true],
+ ['fc00::1', null, true],
+ ['fd00::1', null, true],
+ ['10.0.0.1', '10.0.0.0/24', true],
+ ['10.0.0.1', '10.0.0.1', true],
+ ['fc00::1', 'fc00::1/120', true],
+ ['fc00::1', 'fc00::1', true],
+
+ ['172.16.0.1', ['10.0.0.0/8', '192.168.0.0/16'], false],
+ ['fc00::1', ['fe80::/10', '::ffff:0:0/96'], false],
+
+ // public
+ ['104.26.14.6', null, false],
+ ['104.26.14.6', '104.26.14.0/24', true],
+ ['2606:4700:20::681a:e06', null, false],
+ ['2606:4700:20::681a:e06', '2606:4700:20::/43', true],
+
+ // no ipv4/ipv6 at all
+ ['2606:4700:20::681a:e06', '::/0', true],
+ ['104.26.14.6', '0.0.0.0/0', true],
+
+ // weird scenarios (e.g.: when trying to match ipv4 address on ipv6 subnet)
+ ['10.0.0.1', 'fc00::/7', false],
+ ['fc00::1', '10.0.0.0/8', false],
+ ];
+ }
+
+ /**
+ * @dataProvider getBlacklistData
+ */
+ public function testBlacklist(string $ipAddr, $subnets, bool $mustThrow)
+ {
+ $content = 'foo';
+ $url = sprintf('http://%s/', 0 < substr_count($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr);
+
+ if ($mustThrow) {
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage(sprintf('IP "%s" is blacklisted for "%s".', $ipAddr, $url));
+ }
+
+ $previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content);
+ $client = new NoPrivateNetworkHttpClient($previousHttpClient, $subnets);
+ $response = $client->request('GET', $url);
+
+ if (!$mustThrow) {
+ $this->assertEquals($content, $response->getContent());
+ $this->assertEquals(200, $response->getStatusCode());
+ }
+ }
+
+ public function testCustomOnProgressCallback()
+ {
+ $ipAddr = '104.26.14.6';
+ $url = sprintf('http://%s/', $ipAddr);
+ $content = 'foo';
+
+ $executionCount = 0;
+ $customCallback = function (int $dlNow, int $dlSize, array $info) use (&$executionCount): void {
+ ++$executionCount;
+ };
+
+ $previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content);
+ $client = new NoPrivateNetworkHttpClient($previousHttpClient);
+ $response = $client->request('GET', $url, ['on_progress' => $customCallback]);
+
+ $this->assertEquals(1, $executionCount);
+ $this->assertEquals($content, $response->getContent());
+ $this->assertEquals(200, $response->getStatusCode());
+ }
+
+ public function testNonCallableOnProgressCallback()
+ {
+ $ipAddr = '104.26.14.6';
+ $url = sprintf('http://%s/', $ipAddr);
+ $content = 'bar';
+ $customCallback = sprintf('cb_%s', microtime(true));
+
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Option "on_progress" must be callable, string given.');
+
+ $client = new NoPrivateNetworkHttpClient(new MockHttpClient());
+ $client->request('GET', $url, ['on_progress' => $customCallback]);
+ }
+
+ public function testConstructor()
+ {
+ $this->expectException(\TypeError::class);
+ $this->expectExceptionMessage('Argument 2 passed to Symfony\Component\HttpClient\NoPrivateNetworkHttpClient::__construct() must be of the type array, string or null. integer given.');
+
+ new NoPrivateNetworkHttpClient(new MockHttpClient(), 3);
+ }
+
+ private function getHttpClientMock(string $url, string $ipAddr, string $content)
+ {
+ $previousHttpClient = $this
+ ->getMockBuilder(HttpClientInterface::class)
+ ->getMock();
+
+ $previousHttpClient
+ ->expects($this->once())
+ ->method('request')
+ ->with(
+ 'GET',
+ $url,
+ $this->callback(function ($options) {
+ $this->assertArrayHasKey('on_progress', $options);
+ $onProgress = $options['on_progress'];
+ $this->assertIsCallable($onProgress);
+
+ return true;
+ })
+ )
+ ->willReturnCallback(function ($method, $url, $options) use ($ipAddr, $content): ResponseInterface {
+ $info = [
+ 'primary_ip' => $ipAddr,
+ 'url' => $url,
+ ];
+
+ $onProgress = $options['on_progress'];
+ $onProgress(0, 0, $info);
+
+ return MockResponse::fromRequest($method, $url, [], new MockResponse($content));
+ });
+
+ return $previousHttpClient;
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Tests/TestLogger.php b/src/Symfony/Component/HttpClient/Tests/TestLogger.php
new file mode 100644
index 0000000000000..83aa096889fa0
--- /dev/null
+++ b/src/Symfony/Component/HttpClient/Tests/TestLogger.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpClient\Tests;
+
+use Psr\Log\AbstractLogger;
+
+class TestLogger extends AbstractLogger
+{
+ public $logs = [];
+
+ public function log($level, $message, array $context = []): void
+ {
+ $this->logs[] = $message;
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php
index 949d8afcff85a..181cc84a36c0f 100755
--- a/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php
@@ -36,10 +36,10 @@ public function testItTracesRequest()
return true;
})
)
- ->willReturn(MockResponse::fromRequest('GET', '/foo/bar', ['options1' => 'foo'], new MockResponse()))
+ ->willReturn(MockResponse::fromRequest('GET', '/foo/bar', ['options1' => 'foo'], new MockResponse('hello')))
;
$sut = new TraceableHttpClient($httpClient);
- $sut->request('GET', '/foo/bar', ['options1' => 'foo']);
+ $sut->request('GET', '/foo/bar', ['options1' => 'foo'])->getContent();
$this->assertCount(1, $tracedRequests = $sut->getTracedRequests());
$actualTracedRequest = $tracedRequests[0];
$this->assertEquals([
@@ -47,6 +47,7 @@ public function testItTracesRequest()
'url' => '/foo/bar',
'options' => ['options1' => 'foo'],
'info' => [],
+ 'content' => 'hello',
], $actualTracedRequest);
}
diff --git a/src/Symfony/Component/HttpClient/TraceableHttpClient.php b/src/Symfony/Component/HttpClient/TraceableHttpClient.php
index d60d0849cd95e..70e22091d695c 100644
--- a/src/Symfony/Component/HttpClient/TraceableHttpClient.php
+++ b/src/Symfony/Component/HttpClient/TraceableHttpClient.php
@@ -11,6 +11,9 @@
namespace Symfony\Component\HttpClient;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpClient\Response\TraceableResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
@@ -19,7 +22,7 @@
/**
* @author Jérémy Romey
*/
-final class TraceableHttpClient implements HttpClientInterface, ResetInterface
+final class TraceableHttpClient implements HttpClientInterface, ResetInterface, LoggerAwareInterface
{
private $client;
private $tracedRequests = [];
@@ -34,12 +37,14 @@ public function __construct(HttpClientInterface $client)
*/
public function request(string $method, string $url, array $options = []): ResponseInterface
{
+ $content = null;
$traceInfo = [];
$this->tracedRequests[] = [
'method' => $method,
'url' => $url,
'options' => $options,
'info' => &$traceInfo,
+ 'content' => &$content,
];
$onProgress = $options['on_progress'] ?? null;
@@ -51,7 +56,7 @@ public function request(string $method, string $url, array $options = []): Respo
}
};
- return $this->client->request($method, $url, $options);
+ return new TraceableResponse($this->client, $this->client->request($method, $url, $options), $content);
}
/**
@@ -59,7 +64,21 @@ public function request(string $method, string $url, array $options = []): Respo
*/
public function stream($responses, float $timeout = null): ResponseStreamInterface
{
- return $this->client->stream($responses, $timeout);
+ if ($responses instanceof TraceableResponse) {
+ $responses = [$responses];
+ } elseif (!is_iterable($responses)) {
+ throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of TraceableResponse objects, %s given.', __METHOD__, \is_object($responses) ? \get_class($responses) : \gettype($responses)));
+ }
+
+ return $this->client->stream(\Closure::bind(static function () use ($responses) {
+ foreach ($responses as $k => $r) {
+ if (!$r instanceof TraceableResponse) {
+ throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of TraceableResponse objects, %s given.', __METHOD__, \is_object($r) ? \get_class($r) : \gettype($r)));
+ }
+
+ yield $k => $r->response;
+ }
+ }, null, TraceableResponse::class), $timeout);
}
public function getTracedRequests(): array
@@ -75,4 +94,14 @@ public function reset()
$this->tracedRequests = [];
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLogger(LoggerInterface $logger): void
+ {
+ if ($this->client instanceof LoggerAwareInterface) {
+ $this->client->setLogger($logger);
+ }
+ }
}
diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json
index 68169d9da90fb..e6fadb19416c9 100644
--- a/src/Symfony/Component/HttpClient/composer.json
+++ b/src/Symfony/Component/HttpClient/composer.json
@@ -21,20 +21,23 @@
"symfony/http-client-implementation": "1.1"
},
"require": {
- "php": "^7.1.3",
+ "php": "^7.2.5",
"psr/log": "^1.0",
"symfony/http-client-contracts": "^1.1.8|^2",
"symfony/polyfill-php73": "^1.11",
"symfony/service-contracts": "^1.0|^2"
},
"require-dev": {
+ "amphp/http-client": "^4.2",
+ "amphp/http-tunnel": "^1.0",
+ "amphp/socket": "^1.1",
"guzzlehttp/promises": "^1.3.1",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
- "symfony/dependency-injection": "^4.3|^5.0",
- "symfony/http-kernel": "^4.4",
- "symfony/process": "^4.2|^5.0"
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/http-kernel": "^4.4|^5.0",
+ "symfony/process": "^4.4|^5.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\HttpClient\\": "" },
@@ -45,7 +48,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/src/Symfony/Component/HttpFoundation/AcceptHeader.php
index bbbd62a6d28ab..90f9f479d5958 100644
--- a/src/Symfony/Component/HttpFoundation/AcceptHeader.php
+++ b/src/Symfony/Component/HttpFoundation/AcceptHeader.php
@@ -44,15 +44,13 @@ public function __construct(array $items)
/**
* Builds an AcceptHeader instance from a string.
*
- * @param string $headerValue
- *
* @return self
*/
- public static function fromString($headerValue)
+ public static function fromString(?string $headerValue)
{
$index = 0;
- $parts = HeaderUtils::split((string) $headerValue, ',;=');
+ $parts = HeaderUtils::split($headerValue ?? '', ',;=');
return new self(array_map(function ($subParts) use (&$index) {
$part = array_shift($subParts);
@@ -78,11 +76,9 @@ public function __toString()
/**
* Tests if header has given value.
*
- * @param string $value
- *
* @return bool
*/
- public function has($value)
+ public function has(string $value)
{
return isset($this->items[$value]);
}
@@ -90,11 +86,9 @@ public function has($value)
/**
* Returns given value's item, if exists.
*
- * @param string $value
- *
* @return AcceptHeaderItem|null
*/
- public function get($value)
+ public function get(string $value)
{
return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null;
}
@@ -127,11 +121,9 @@ public function all()
/**
* Filters items on their value using given regex.
*
- * @param string $pattern
- *
* @return self
*/
- public function filter($pattern)
+ public function filter(string $pattern)
{
return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
return preg_match($pattern, $item->getValue());
diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php b/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php
index 954aac6014544..bc4014e581693 100644
--- a/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php
+++ b/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php
@@ -34,13 +34,11 @@ public function __construct(string $value, array $attributes = [])
/**
* Builds an AcceptHeaderInstance instance from a string.
*
- * @param string $itemValue
- *
* @return self
*/
- public static function fromString($itemValue)
+ public static function fromString(?string $itemValue)
{
- $parts = HeaderUtils::split($itemValue, ';=');
+ $parts = HeaderUtils::split($itemValue ?? '', ';=');
$part = array_shift($parts);
$attributes = HeaderUtils::combine($parts);
@@ -66,11 +64,9 @@ public function __toString()
/**
* Set the item value.
*
- * @param string $value
- *
* @return $this
*/
- public function setValue($value)
+ public function setValue(string $value)
{
$this->value = $value;
@@ -90,11 +86,9 @@ public function getValue()
/**
* Set the item quality.
*
- * @param float $quality
- *
* @return $this
*/
- public function setQuality($quality)
+ public function setQuality(float $quality)
{
$this->quality = $quality;
@@ -114,11 +108,9 @@ public function getQuality()
/**
* Set the item index.
*
- * @param int $index
- *
* @return $this
*/
- public function setIndex($index)
+ public function setIndex(int $index)
{
$this->index = $index;
@@ -138,11 +130,9 @@ public function getIndex()
/**
* Tests if an attribute exists.
*
- * @param string $name
- *
* @return bool
*/
- public function hasAttribute($name)
+ public function hasAttribute(string $name)
{
return isset($this->attributes[$name]);
}
@@ -150,12 +140,11 @@ public function hasAttribute($name)
/**
* Returns an attribute by its name.
*
- * @param string $name
- * @param mixed $default
+ * @param mixed $default
*
* @return mixed
*/
- public function getAttribute($name, $default = null)
+ public function getAttribute(string $name, $default = null)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
}
@@ -173,17 +162,14 @@ public function getAttributes()
/**
* Set an attribute.
*
- * @param string $name
- * @param string $value
- *
* @return $this
*/
- public function setAttribute($name, $value)
+ public function setAttribute(string $name, string $value)
{
if ('q' === $name) {
$this->quality = (float) $value;
} else {
- $this->attributes[$name] = (string) $value;
+ $this->attributes[$name] = $value;
}
return $this;
diff --git a/src/Symfony/Component/HttpFoundation/ApacheRequest.php b/src/Symfony/Component/HttpFoundation/ApacheRequest.php
deleted file mode 100644
index f189cde585b18..0000000000000
--- a/src/Symfony/Component/HttpFoundation/ApacheRequest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ApacheRequest::class, Request::class), E_USER_DEPRECATED);
-
-/**
- * Request represents an HTTP request from an Apache server.
- *
- * @deprecated since Symfony 4.4. Use the Request class instead.
- *
- * @author Fabien Potencier
- */
-class ApacheRequest extends Request
-{
- /**
- * {@inheritdoc}
- */
- protected function prepareRequestUri()
- {
- return $this->server->get('REQUEST_URI');
- }
-
- /**
- * {@inheritdoc}
- */
- protected function prepareBaseUrl()
- {
- $baseUrl = $this->server->get('SCRIPT_NAME');
-
- if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
- // assume mod_rewrite
- return rtrim(\dirname($baseUrl), '/\\');
- }
-
- return $baseUrl;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
index 7bdbf4def3994..30eff944d750a 100644
--- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
+++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
@@ -66,7 +66,7 @@ public function __construct($file, int $status = 200, array $headers = [], bool
*
* @return static
*/
- public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
{
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
}
@@ -74,16 +74,13 @@ public static function create($file = null, $status = 200, $headers = [], $publi
/**
* Sets the file to stream.
*
- * @param \SplFileInfo|string $file The file to stream
- * @param string $contentDisposition
- * @param bool $autoEtag
- * @param bool $autoLastModified
+ * @param \SplFileInfo|string $file The file to stream
*
* @return $this
*
* @throws FileException
*/
- public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ public function setFile($file, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
{
if (!$file instanceof File) {
if ($file instanceof \SplFileInfo) {
@@ -153,7 +150,7 @@ public function setAutoEtag()
*
* @return $this
*/
- public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
+ public function setContentDisposition(string $disposition, string $filename = '', string $filenameFallback = '')
{
if ('' === $filename) {
$filename = $this->file->getFilename();
@@ -317,7 +314,7 @@ public function sendContent()
*
* @throws \LogicException when the content is not null
*/
- public function setContent($content)
+ public function setContent(?string $content)
{
if (null !== $content) {
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
@@ -346,11 +343,9 @@ public static function trustXSendfileTypeHeader()
* If this is set to true, the file will be unlinked after the request is sent
* Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
*
- * @param bool $shouldDelete
- *
* @return $this
*/
- public function deleteFileAfterSend($shouldDelete = true)
+ public function deleteFileAfterSend(bool $shouldDelete = true)
{
$this->deleteFileAfterSend = $shouldDelete;
diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md
index 3fa73a26a9074..56b76d84b8c68 100644
--- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md
+++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md
@@ -1,6 +1,30 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * added `Cookie::withValue`, `Cookie::withDomain`, `Cookie::withExpires`,
+ `Cookie::withPath`, `Cookie::withSecure`, `Cookie::withHttpOnly`,
+ `Cookie::withRaw`, `Cookie::withSameSite`
+ * Deprecate `Response::create()`, `JsonResponse::create()`,
+ `RedirectResponse::create()`, and `StreamedResponse::create()` methods (use
+ `__construct()` instead)
+ * added `Request::preferSafeContent()` and `Response::setContentSafe()` to handle "safe" HTTP preference
+ according to [RFC 8674](https://tools.ietf.org/html/rfc8674)
+ * made the Mime component an optional dependency
+ * added `MarshallingSessionHandler`, `IdentityMarshaller`
+
+5.0.0
+-----
+
+ * made `Cookie` auto-secure and lax by default
+ * removed classes in the `MimeType` namespace, use the Symfony Mime component instead
+ * removed method `UploadedFile::getClientSize()` and the related constructor argument
+ * made `Request::getSession()` throw if the session has not been set before
+ * removed `Response::HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL`
+ * passing a null url when instantiating a `RedirectResponse` is not allowed
+
4.4.0
-----
diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php
index 1e22c745a03fb..fa03ac3b9f39a 100644
--- a/src/Symfony/Component/HttpFoundation/Cookie.php
+++ b/src/Symfony/Component/HttpFoundation/Cookie.php
@@ -41,12 +41,9 @@ class Cookie
/**
* Creates cookie from raw header string.
*
- * @param string $cookie
- * @param bool $decode
- *
* @return static
*/
- public static function fromString($cookie, $decode = false)
+ public static function fromString(string $cookie, bool $decode = false)
{
$data = [
'expires' => 0,
@@ -91,12 +88,8 @@ public static function create(string $name, string $value = null, $expire = 0, ?
*
* @throws \InvalidArgumentException
*/
- public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
+ public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = 'lax')
{
- if (9 > \func_num_args()) {
- @trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED);
- }
-
// from PHP source code
if ($raw && false !== strpbrk($name, self::$reservedCharsList)) {
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
@@ -106,6 +99,52 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
throw new \InvalidArgumentException('The cookie name cannot be empty.');
}
+ $this->name = $name;
+ $this->value = $value;
+ $this->domain = $domain;
+ $this->expire = $this->withExpires($expire)->expire;
+ $this->path = empty($path) ? '/' : $path;
+ $this->secure = $secure;
+ $this->httpOnly = $httpOnly;
+ $this->raw = $raw;
+ $this->sameSite = $this->withSameSite($sameSite)->sameSite;
+ }
+
+ /**
+ * Creates a cookie copy with a new value.
+ *
+ * @return static
+ */
+ public function withValue(?string $value): self
+ {
+ $cookie = clone $this;
+ $cookie->value = $value;
+
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy with a new domain that the cookie is available to.
+ *
+ * @return static
+ */
+ public function withDomain(?string $domain): self
+ {
+ $cookie = clone $this;
+ $cookie->domain = $domain;
+
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy with a new time the cookie expires.
+ *
+ * @param int|string|\DateTimeInterface $expire
+ *
+ * @return static
+ */
+ public function withExpires($expire = 0): self
+ {
// convert expiration time to a Unix timestamp
if ($expire instanceof \DateTimeInterface) {
$expire = $expire->format('U');
@@ -117,15 +156,75 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
}
}
- $this->name = $name;
- $this->value = $value;
- $this->domain = $domain;
- $this->expire = 0 < $expire ? (int) $expire : 0;
- $this->path = empty($path) ? '/' : $path;
- $this->secure = $secure;
- $this->httpOnly = $httpOnly;
- $this->raw = $raw;
+ $cookie = clone $this;
+ $cookie->expire = 0 < $expire ? (int) $expire : 0;
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy with a new path on the server in which the cookie will be available on.
+ *
+ * @return static
+ */
+ public function withPath(string $path): self
+ {
+ $cookie = clone $this;
+ $cookie->path = '' === $path ? '/' : $path;
+
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy that only be transmitted over a secure HTTPS connection from the client.
+ *
+ * @return static
+ */
+ public function withSecure(bool $secure = true): self
+ {
+ $cookie = clone $this;
+ $cookie->secure = $secure;
+
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy that be accessible only through the HTTP protocol.
+ *
+ * @return static
+ */
+ public function withHttpOnly(bool $httpOnly = true): self
+ {
+ $cookie = clone $this;
+ $cookie->httpOnly = $httpOnly;
+
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy that uses no url encoding.
+ *
+ * @return static
+ */
+ public function withRaw(bool $raw = true): self
+ {
+ if ($raw && false !== strpbrk($this->name, self::$reservedCharsList)) {
+ throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $this->name));
+ }
+
+ $cookie = clone $this;
+ $cookie->raw = $raw;
+
+ return $cookie;
+ }
+
+ /**
+ * Creates a cookie copy with SameSite attribute.
+ *
+ * @return static
+ */
+ public function withSameSite(?string $sameSite): self
+ {
if ('' === $sameSite) {
$sameSite = null;
} elseif (null !== $sameSite) {
@@ -136,7 +235,10 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
}
- $this->sameSite = $sameSite;
+ $cookie = clone $this;
+ $cookie->sameSite = $sameSite;
+
+ return $cookie;
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php
index 4906588a72aa1..e4df5f4df0252 100644
--- a/src/Symfony/Component/HttpFoundation/File/File.php
+++ b/src/Symfony/Component/HttpFoundation/File/File.php
@@ -54,6 +54,10 @@ public function __construct(string $path, bool $checkPath = true)
*/
public function guessExtension()
{
+ if (!class_exists(MimeTypes::class)) {
+ throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
+ }
+
return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;
}
@@ -70,20 +74,21 @@ public function guessExtension()
*/
public function getMimeType()
{
+ if (!class_exists(MimeTypes::class)) {
+ throw new \LogicException('You cannot guess the mime type as the Mime component is not installed. Try running "composer require symfony/mime".');
+ }
+
return MimeTypes::getDefault()->guessMimeType($this->getPathname());
}
/**
* Moves the file to a new location.
*
- * @param string $directory The destination folder
- * @param string $name The new file name
- *
* @return self A File object representing the new file
*
* @throws FileException if the target file could not be created
*/
- public function move($directory, $name = null)
+ public function move(string $directory, string $name = null)
{
$target = $this->getTargetFile($directory, $name);
@@ -102,7 +107,7 @@ public function move($directory, $name = null)
/**
* @return self
*/
- protected function getTargetFile($directory, $name = null)
+ protected function getTargetFile(string $directory, string $name = null)
{
if (!is_dir($directory)) {
if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
@@ -120,11 +125,9 @@ protected function getTargetFile($directory, $name = null)
/**
* Returns locale independent base name of the given path.
*
- * @param string $name The new file name
- *
* @return string
*/
- protected function getName($name)
+ protected function getName(string $name)
{
$originalName = str_replace('\\', '/', $name);
$pos = strrpos($originalName, '/');
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php
deleted file mode 100644
index 4ac2013305c0e..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php
+++ /dev/null
@@ -1,102 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\Mime\MimeTypes;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', ExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
-
-/**
- * A singleton mime type to file extension guesser.
- *
- * A default guesser is provided.
- * You can register custom guessers by calling the register()
- * method on the singleton instance:
- *
- * $guesser = ExtensionGuesser::getInstance();
- * $guesser->register(new MyCustomExtensionGuesser());
- *
- * The last registered guesser is preferred over previously registered ones.
- *
- * @deprecated since Symfony 4.3, use {@link MimeTypes} instead
- */
-class ExtensionGuesser implements ExtensionGuesserInterface
-{
- /**
- * The singleton instance.
- *
- * @var ExtensionGuesser
- */
- private static $instance = null;
-
- /**
- * All registered ExtensionGuesserInterface instances.
- *
- * @var array
- */
- protected $guessers = [];
-
- /**
- * Returns the singleton instance.
- *
- * @return self
- */
- public static function getInstance()
- {
- if (null === self::$instance) {
- self::$instance = new self();
- }
-
- return self::$instance;
- }
-
- /**
- * Registers all natively provided extension guessers.
- */
- private function __construct()
- {
- $this->register(new MimeTypeExtensionGuesser());
- }
-
- /**
- * Registers a new extension guesser.
- *
- * When guessing, this guesser is preferred over previously registered ones.
- */
- public function register(ExtensionGuesserInterface $guesser)
- {
- array_unshift($this->guessers, $guesser);
- }
-
- /**
- * Tries to guess the extension.
- *
- * The mime type is passed to each registered mime type guesser in reverse order
- * of their registration (last registered is queried first). Once a guesser
- * returns a value that is not NULL, this method terminates and returns the
- * value.
- *
- * @param string $mimeType The mime type
- *
- * @return string The guessed extension or NULL, if none could be guessed
- */
- public function guess($mimeType)
- {
- foreach ($this->guessers as $guesser) {
- if (null !== $extension = $guesser->guess($mimeType)) {
- return $extension;
- }
- }
-
- return null;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php b/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php
deleted file mode 100644
index 69fe6efb286c4..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\Mime\MimeTypesInterface;
-
-/**
- * Guesses the file extension corresponding to a given mime type.
- *
- * @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
- */
-interface ExtensionGuesserInterface
-{
- /**
- * Makes a best guess for a file extension, given a mime type.
- *
- * @param string $mimeType The mime type
- *
- * @return string The guessed extension or NULL, if none could be guessed
- */
- public function guess($mimeType);
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
deleted file mode 100644
index 5d3ae106440cc..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
-use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
-use Symfony\Component\Mime\FileBinaryMimeTypeGuesser as NewFileBinaryMimeTypeGuesser;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileBinaryMimeTypeGuesser::class, NewFileBinaryMimeTypeGuesser::class), E_USER_DEPRECATED);
-
-/**
- * Guesses the mime type with the binary "file" (only available on *nix).
- *
- * @author Bernhard Schussek
- *
- * @deprecated since Symfony 4.3, use {@link NewFileBinaryMimeTypeGuesser} instead
- */
-class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
-{
- private $cmd;
-
- /**
- * The $cmd pattern must contain a "%s" string that will be replaced
- * with the file name to guess.
- *
- * The command output must start with the mime type of the file.
- *
- * @param string $cmd The command to run to get the mime type of a file
- */
- public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null')
- {
- $this->cmd = $cmd;
- }
-
- /**
- * Returns whether this guesser is supported on the current OS.
- *
- * @return bool
- */
- public static function isSupported()
- {
- static $supported = null;
-
- if (null !== $supported) {
- return $supported;
- }
-
- if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) {
- return $supported = false;
- }
-
- ob_start();
- passthru('command -v file', $exitStatus);
- $binPath = trim(ob_get_clean());
-
- return $supported = 0 === $exitStatus && '' !== $binPath;
- }
-
- /**
- * {@inheritdoc}
- */
- public function guess($path)
- {
- if (!is_file($path)) {
- throw new FileNotFoundException($path);
- }
-
- if (!is_readable($path)) {
- throw new AccessDeniedException($path);
- }
-
- if (!self::isSupported()) {
- return null;
- }
-
- ob_start();
-
- // need to use --mime instead of -i. see #6641
- passthru(sprintf($this->cmd, escapeshellarg((0 === strpos($path, '-') ? './' : '').$path)), $return);
- if ($return > 0) {
- ob_end_clean();
-
- return null;
- }
-
- $type = trim(ob_get_clean());
-
- if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) {
- // it's not a type, but an error message
- return null;
- }
-
- return $match[1];
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php
deleted file mode 100644
index 70a01d7aecd0d..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php
+++ /dev/null
@@ -1,74 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
-use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
-use Symfony\Component\Mime\FileinfoMimeTypeGuesser as NewFileinfoMimeTypeGuesser;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileinfoMimeTypeGuesser::class, NewFileinfoMimeTypeGuesser::class), E_USER_DEPRECATED);
-
-/**
- * Guesses the mime type using the PECL extension FileInfo.
- *
- * @author Bernhard Schussek
- *
- * @deprecated since Symfony 4.3, use {@link NewFileinfoMimeTypeGuesser} instead
- */
-class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
-{
- private $magicFile;
-
- /**
- * @param string $magicFile A magic file to use with the finfo instance
- *
- * @see https://php.net/finfo-open
- */
- public function __construct(string $magicFile = null)
- {
- $this->magicFile = $magicFile;
- }
-
- /**
- * Returns whether this guesser is supported on the current OS/PHP setup.
- *
- * @return bool
- */
- public static function isSupported()
- {
- return \function_exists('finfo_open');
- }
-
- /**
- * {@inheritdoc}
- */
- public function guess($path)
- {
- if (!is_file($path)) {
- throw new FileNotFoundException($path);
- }
-
- if (!is_readable($path)) {
- throw new AccessDeniedException($path);
- }
-
- if (!self::isSupported()) {
- return null;
- }
-
- if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
- return null;
- }
-
- return $finfo->file($path);
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php
deleted file mode 100644
index 651be070e122b..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php
+++ /dev/null
@@ -1,826 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\Mime\MimeTypes;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
-
-/**
- * Provides a best-guess mapping of mime type to file extension.
- *
- * @deprecated since Symfony 4.3, use {@link MimeTypes} instead
- */
-class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
-{
- /**
- * A map of mime types and their default extensions.
- *
- * This list has been placed under the public domain by the Apache HTTPD project.
- * This list has been updated from upstream on 2019-01-14.
- *
- * @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
- */
- protected $defaultExtensions = [
- 'application/andrew-inset' => 'ez',
- 'application/applixware' => 'aw',
- 'application/atom+xml' => 'atom',
- 'application/atomcat+xml' => 'atomcat',
- 'application/atomsvc+xml' => 'atomsvc',
- 'application/ccxml+xml' => 'ccxml',
- 'application/cdmi-capability' => 'cdmia',
- 'application/cdmi-container' => 'cdmic',
- 'application/cdmi-domain' => 'cdmid',
- 'application/cdmi-object' => 'cdmio',
- 'application/cdmi-queue' => 'cdmiq',
- 'application/cu-seeme' => 'cu',
- 'application/davmount+xml' => 'davmount',
- 'application/docbook+xml' => 'dbk',
- 'application/dssc+der' => 'dssc',
- 'application/dssc+xml' => 'xdssc',
- 'application/ecmascript' => 'ecma',
- 'application/emma+xml' => 'emma',
- 'application/epub+zip' => 'epub',
- 'application/exi' => 'exi',
- 'application/font-tdpfr' => 'pfr',
- 'application/gml+xml' => 'gml',
- 'application/gpx+xml' => 'gpx',
- 'application/gxf' => 'gxf',
- 'application/hyperstudio' => 'stk',
- 'application/inkml+xml' => 'ink',
- 'application/ipfix' => 'ipfix',
- 'application/java-archive' => 'jar',
- 'application/java-serialized-object' => 'ser',
- 'application/java-vm' => 'class',
- 'application/javascript' => 'js',
- 'application/json' => 'json',
- 'application/jsonml+json' => 'jsonml',
- 'application/lost+xml' => 'lostxml',
- 'application/mac-binhex40' => 'hqx',
- 'application/mac-compactpro' => 'cpt',
- 'application/mads+xml' => 'mads',
- 'application/marc' => 'mrc',
- 'application/marcxml+xml' => 'mrcx',
- 'application/mathematica' => 'ma',
- 'application/mathml+xml' => 'mathml',
- 'application/mbox' => 'mbox',
- 'application/mediaservercontrol+xml' => 'mscml',
- 'application/metalink+xml' => 'metalink',
- 'application/metalink4+xml' => 'meta4',
- 'application/mets+xml' => 'mets',
- 'application/mods+xml' => 'mods',
- 'application/mp21' => 'm21',
- 'application/mp4' => 'mp4s',
- 'application/msword' => 'doc',
- 'application/mxf' => 'mxf',
- 'application/octet-stream' => 'bin',
- 'application/oda' => 'oda',
- 'application/oebps-package+xml' => 'opf',
- 'application/ogg' => 'ogx',
- 'application/omdoc+xml' => 'omdoc',
- 'application/onenote' => 'onetoc',
- 'application/oxps' => 'oxps',
- 'application/patch-ops-error+xml' => 'xer',
- 'application/pdf' => 'pdf',
- 'application/pgp-encrypted' => 'pgp',
- 'application/pgp-signature' => 'asc',
- 'application/pics-rules' => 'prf',
- 'application/pkcs10' => 'p10',
- 'application/pkcs7-mime' => 'p7m',
- 'application/pkcs7-signature' => 'p7s',
- 'application/pkcs8' => 'p8',
- 'application/pkix-attr-cert' => 'ac',
- 'application/pkix-cert' => 'cer',
- 'application/pkix-crl' => 'crl',
- 'application/pkix-pkipath' => 'pkipath',
- 'application/pkixcmp' => 'pki',
- 'application/pls+xml' => 'pls',
- 'application/postscript' => 'ai',
- 'application/prs.cww' => 'cww',
- 'application/pskc+xml' => 'pskcxml',
- 'application/rdf+xml' => 'rdf',
- 'application/reginfo+xml' => 'rif',
- 'application/relax-ng-compact-syntax' => 'rnc',
- 'application/resource-lists+xml' => 'rl',
- 'application/resource-lists-diff+xml' => 'rld',
- 'application/rls-services+xml' => 'rs',
- 'application/rpki-ghostbusters' => 'gbr',
- 'application/rpki-manifest' => 'mft',
- 'application/rpki-roa' => 'roa',
- 'application/rsd+xml' => 'rsd',
- 'application/rss+xml' => 'rss',
- 'application/rtf' => 'rtf',
- 'application/sbml+xml' => 'sbml',
- 'application/scvp-cv-request' => 'scq',
- 'application/scvp-cv-response' => 'scs',
- 'application/scvp-vp-request' => 'spq',
- 'application/scvp-vp-response' => 'spp',
- 'application/sdp' => 'sdp',
- 'application/set-payment-initiation' => 'setpay',
- 'application/set-registration-initiation' => 'setreg',
- 'application/shf+xml' => 'shf',
- 'application/smil+xml' => 'smi',
- 'application/sparql-query' => 'rq',
- 'application/sparql-results+xml' => 'srx',
- 'application/srgs' => 'gram',
- 'application/srgs+xml' => 'grxml',
- 'application/sru+xml' => 'sru',
- 'application/ssdl+xml' => 'ssdl',
- 'application/ssml+xml' => 'ssml',
- 'application/tei+xml' => 'tei',
- 'application/thraud+xml' => 'tfi',
- 'application/timestamped-data' => 'tsd',
- 'application/vnd.3gpp.pic-bw-large' => 'plb',
- 'application/vnd.3gpp.pic-bw-small' => 'psb',
- 'application/vnd.3gpp.pic-bw-var' => 'pvb',
- 'application/vnd.3gpp2.tcap' => 'tcap',
- 'application/vnd.3m.post-it-notes' => 'pwn',
- 'application/vnd.accpac.simply.aso' => 'aso',
- 'application/vnd.accpac.simply.imp' => 'imp',
- 'application/vnd.acucobol' => 'acu',
- 'application/vnd.acucorp' => 'atc',
- 'application/vnd.adobe.air-application-installer-package+zip' => 'air',
- 'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
- 'application/vnd.adobe.fxp' => 'fxp',
- 'application/vnd.adobe.xdp+xml' => 'xdp',
- 'application/vnd.adobe.xfdf' => 'xfdf',
- 'application/vnd.ahead.space' => 'ahead',
- 'application/vnd.airzip.filesecure.azf' => 'azf',
- 'application/vnd.airzip.filesecure.azs' => 'azs',
- 'application/vnd.amazon.ebook' => 'azw',
- 'application/vnd.americandynamics.acc' => 'acc',
- 'application/vnd.amiga.ami' => 'ami',
- 'application/vnd.android.package-archive' => 'apk',
- 'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
- 'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
- 'application/vnd.antix.game-component' => 'atx',
- 'application/vnd.apple.installer+xml' => 'mpkg',
- 'application/vnd.apple.mpegurl' => 'm3u8',
- 'application/vnd.aristanetworks.swi' => 'swi',
- 'application/vnd.astraea-software.iota' => 'iota',
- 'application/vnd.audiograph' => 'aep',
- 'application/vnd.blueice.multipass' => 'mpm',
- 'application/vnd.bmi' => 'bmi',
- 'application/vnd.businessobjects' => 'rep',
- 'application/vnd.chemdraw+xml' => 'cdxml',
- 'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
- 'application/vnd.cinderella' => 'cdy',
- 'application/vnd.claymore' => 'cla',
- 'application/vnd.cloanto.rp9' => 'rp9',
- 'application/vnd.clonk.c4group' => 'c4g',
- 'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
- 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
- 'application/vnd.commonspace' => 'csp',
- 'application/vnd.contact.cmsg' => 'cdbcmsg',
- 'application/vnd.cosmocaller' => 'cmc',
- 'application/vnd.crick.clicker' => 'clkx',
- 'application/vnd.crick.clicker.keyboard' => 'clkk',
- 'application/vnd.crick.clicker.palette' => 'clkp',
- 'application/vnd.crick.clicker.template' => 'clkt',
- 'application/vnd.crick.clicker.wordbank' => 'clkw',
- 'application/vnd.criticaltools.wbs+xml' => 'wbs',
- 'application/vnd.ctc-posml' => 'pml',
- 'application/vnd.cups-ppd' => 'ppd',
- 'application/vnd.curl.car' => 'car',
- 'application/vnd.curl.pcurl' => 'pcurl',
- 'application/vnd.dart' => 'dart',
- 'application/vnd.data-vision.rdz' => 'rdz',
- 'application/vnd.dece.data' => 'uvf',
- 'application/vnd.dece.ttml+xml' => 'uvt',
- 'application/vnd.dece.unspecified' => 'uvx',
- 'application/vnd.dece.zip' => 'uvz',
- 'application/vnd.denovo.fcselayout-link' => 'fe_launch',
- 'application/vnd.dna' => 'dna',
- 'application/vnd.dolby.mlp' => 'mlp',
- 'application/vnd.dpgraph' => 'dpg',
- 'application/vnd.dreamfactory' => 'dfac',
- 'application/vnd.ds-keypoint' => 'kpxx',
- 'application/vnd.dvb.ait' => 'ait',
- 'application/vnd.dvb.service' => 'svc',
- 'application/vnd.dynageo' => 'geo',
- 'application/vnd.ecowin.chart' => 'mag',
- 'application/vnd.enliven' => 'nml',
- 'application/vnd.epson.esf' => 'esf',
- 'application/vnd.epson.msf' => 'msf',
- 'application/vnd.epson.quickanime' => 'qam',
- 'application/vnd.epson.salt' => 'slt',
- 'application/vnd.epson.ssf' => 'ssf',
- 'application/vnd.eszigno3+xml' => 'es3',
- 'application/vnd.ezpix-album' => 'ez2',
- 'application/vnd.ezpix-package' => 'ez3',
- 'application/vnd.fdf' => 'fdf',
- 'application/vnd.fdsn.mseed' => 'mseed',
- 'application/vnd.fdsn.seed' => 'seed',
- 'application/vnd.flographit' => 'gph',
- 'application/vnd.fluxtime.clip' => 'ftc',
- 'application/vnd.framemaker' => 'fm',
- 'application/vnd.frogans.fnc' => 'fnc',
- 'application/vnd.frogans.ltf' => 'ltf',
- 'application/vnd.fsc.weblaunch' => 'fsc',
- 'application/vnd.fujitsu.oasys' => 'oas',
- 'application/vnd.fujitsu.oasys2' => 'oa2',
- 'application/vnd.fujitsu.oasys3' => 'oa3',
- 'application/vnd.fujitsu.oasysgp' => 'fg5',
- 'application/vnd.fujitsu.oasysprs' => 'bh2',
- 'application/vnd.fujixerox.ddd' => 'ddd',
- 'application/vnd.fujixerox.docuworks' => 'xdw',
- 'application/vnd.fujixerox.docuworks.binder' => 'xbd',
- 'application/vnd.fuzzysheet' => 'fzs',
- 'application/vnd.genomatix.tuxedo' => 'txd',
- 'application/vnd.geogebra.file' => 'ggb',
- 'application/vnd.geogebra.tool' => 'ggt',
- 'application/vnd.geometry-explorer' => 'gex',
- 'application/vnd.geonext' => 'gxt',
- 'application/vnd.geoplan' => 'g2w',
- 'application/vnd.geospace' => 'g3w',
- 'application/vnd.gmx' => 'gmx',
- 'application/vnd.google-earth.kml+xml' => 'kml',
- 'application/vnd.google-earth.kmz' => 'kmz',
- 'application/vnd.grafeq' => 'gqf',
- 'application/vnd.groove-account' => 'gac',
- 'application/vnd.groove-help' => 'ghf',
- 'application/vnd.groove-identity-message' => 'gim',
- 'application/vnd.groove-injector' => 'grv',
- 'application/vnd.groove-tool-message' => 'gtm',
- 'application/vnd.groove-tool-template' => 'tpl',
- 'application/vnd.groove-vcard' => 'vcg',
- 'application/vnd.hal+xml' => 'hal',
- 'application/vnd.handheld-entertainment+xml' => 'zmm',
- 'application/vnd.hbci' => 'hbci',
- 'application/vnd.hhe.lesson-player' => 'les',
- 'application/vnd.hp-hpgl' => 'hpgl',
- 'application/vnd.hp-hpid' => 'hpid',
- 'application/vnd.hp-hps' => 'hps',
- 'application/vnd.hp-jlyt' => 'jlt',
- 'application/vnd.hp-pcl' => 'pcl',
- 'application/vnd.hp-pclxl' => 'pclxl',
- 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
- 'application/vnd.ibm.minipay' => 'mpy',
- 'application/vnd.ibm.modcap' => 'afp',
- 'application/vnd.ibm.rights-management' => 'irm',
- 'application/vnd.ibm.secure-container' => 'sc',
- 'application/vnd.iccprofile' => 'icc',
- 'application/vnd.igloader' => 'igl',
- 'application/vnd.immervision-ivp' => 'ivp',
- 'application/vnd.immervision-ivu' => 'ivu',
- 'application/vnd.insors.igm' => 'igm',
- 'application/vnd.intercon.formnet' => 'xpw',
- 'application/vnd.intergeo' => 'i2g',
- 'application/vnd.intu.qbo' => 'qbo',
- 'application/vnd.intu.qfx' => 'qfx',
- 'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
- 'application/vnd.irepository.package+xml' => 'irp',
- 'application/vnd.is-xpr' => 'xpr',
- 'application/vnd.isac.fcs' => 'fcs',
- 'application/vnd.jam' => 'jam',
- 'application/vnd.jcp.javame.midlet-rms' => 'rms',
- 'application/vnd.jisp' => 'jisp',
- 'application/vnd.joost.joda-archive' => 'joda',
- 'application/vnd.kahootz' => 'ktz',
- 'application/vnd.kde.karbon' => 'karbon',
- 'application/vnd.kde.kchart' => 'chrt',
- 'application/vnd.kde.kformula' => 'kfo',
- 'application/vnd.kde.kivio' => 'flw',
- 'application/vnd.kde.kontour' => 'kon',
- 'application/vnd.kde.kpresenter' => 'kpr',
- 'application/vnd.kde.kspread' => 'ksp',
- 'application/vnd.kde.kword' => 'kwd',
- 'application/vnd.kenameaapp' => 'htke',
- 'application/vnd.kidspiration' => 'kia',
- 'application/vnd.kinar' => 'kne',
- 'application/vnd.koan' => 'skp',
- 'application/vnd.kodak-descriptor' => 'sse',
- 'application/vnd.las.las+xml' => 'lasxml',
- 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
- 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
- 'application/vnd.lotus-1-2-3' => '123',
- 'application/vnd.lotus-approach' => 'apr',
- 'application/vnd.lotus-freelance' => 'pre',
- 'application/vnd.lotus-notes' => 'nsf',
- 'application/vnd.lotus-organizer' => 'org',
- 'application/vnd.lotus-screencam' => 'scm',
- 'application/vnd.lotus-wordpro' => 'lwp',
- 'application/vnd.macports.portpkg' => 'portpkg',
- 'application/vnd.mcd' => 'mcd',
- 'application/vnd.medcalcdata' => 'mc1',
- 'application/vnd.mediastation.cdkey' => 'cdkey',
- 'application/vnd.mfer' => 'mwf',
- 'application/vnd.mfmp' => 'mfm',
- 'application/vnd.micrografx.flo' => 'flo',
- 'application/vnd.micrografx.igx' => 'igx',
- 'application/vnd.mif' => 'mif',
- 'application/vnd.mobius.daf' => 'daf',
- 'application/vnd.mobius.dis' => 'dis',
- 'application/vnd.mobius.mbk' => 'mbk',
- 'application/vnd.mobius.mqy' => 'mqy',
- 'application/vnd.mobius.msl' => 'msl',
- 'application/vnd.mobius.plc' => 'plc',
- 'application/vnd.mobius.txf' => 'txf',
- 'application/vnd.mophun.application' => 'mpn',
- 'application/vnd.mophun.certificate' => 'mpc',
- 'application/vnd.mozilla.xul+xml' => 'xul',
- 'application/vnd.ms-artgalry' => 'cil',
- 'application/vnd.ms-cab-compressed' => 'cab',
- 'application/vnd.ms-excel' => 'xls',
- 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
- 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
- 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
- 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
- 'application/vnd.ms-fontobject' => 'eot',
- 'application/vnd.ms-htmlhelp' => 'chm',
- 'application/vnd.ms-ims' => 'ims',
- 'application/vnd.ms-lrm' => 'lrm',
- 'application/vnd.ms-officetheme' => 'thmx',
- 'application/vnd.ms-pki.seccat' => 'cat',
- 'application/vnd.ms-pki.stl' => 'stl',
- 'application/vnd.ms-powerpoint' => 'ppt',
- 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
- 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
- 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
- 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
- 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
- 'application/vnd.ms-project' => 'mpp',
- 'application/vnd.ms-word.document.macroenabled.12' => 'docm',
- 'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
- 'application/vnd.ms-works' => 'wps',
- 'application/vnd.ms-wpl' => 'wpl',
- 'application/vnd.ms-xpsdocument' => 'xps',
- 'application/vnd.mseq' => 'mseq',
- 'application/vnd.musician' => 'mus',
- 'application/vnd.muvee.style' => 'msty',
- 'application/vnd.mynfc' => 'taglet',
- 'application/vnd.neurolanguage.nlu' => 'nlu',
- 'application/vnd.nitf' => 'ntf',
- 'application/vnd.noblenet-directory' => 'nnd',
- 'application/vnd.noblenet-sealer' => 'nns',
- 'application/vnd.noblenet-web' => 'nnw',
- 'application/vnd.nokia.n-gage.data' => 'ngdat',
- 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
- 'application/vnd.nokia.radio-preset' => 'rpst',
- 'application/vnd.nokia.radio-presets' => 'rpss',
- 'application/vnd.novadigm.edm' => 'edm',
- 'application/vnd.novadigm.edx' => 'edx',
- 'application/vnd.novadigm.ext' => 'ext',
- 'application/vnd.oasis.opendocument.chart' => 'odc',
- 'application/vnd.oasis.opendocument.chart-template' => 'otc',
- 'application/vnd.oasis.opendocument.database' => 'odb',
- 'application/vnd.oasis.opendocument.formula' => 'odf',
- 'application/vnd.oasis.opendocument.formula-template' => 'odft',
- 'application/vnd.oasis.opendocument.graphics' => 'odg',
- 'application/vnd.oasis.opendocument.graphics-template' => 'otg',
- 'application/vnd.oasis.opendocument.image' => 'odi',
- 'application/vnd.oasis.opendocument.image-template' => 'oti',
- 'application/vnd.oasis.opendocument.presentation' => 'odp',
- 'application/vnd.oasis.opendocument.presentation-template' => 'otp',
- 'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
- 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
- 'application/vnd.oasis.opendocument.text' => 'odt',
- 'application/vnd.oasis.opendocument.text-master' => 'odm',
- 'application/vnd.oasis.opendocument.text-template' => 'ott',
- 'application/vnd.oasis.opendocument.text-web' => 'oth',
- 'application/vnd.olpc-sugar' => 'xo',
- 'application/vnd.oma.dd2+xml' => 'dd2',
- 'application/vnd.openofficeorg.extension' => 'oxt',
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
- 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
- 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
- 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
- 'application/vnd.osgeo.mapguide.package' => 'mgp',
- 'application/vnd.osgi.dp' => 'dp',
- 'application/vnd.osgi.subsystem' => 'esa',
- 'application/vnd.palm' => 'pdb',
- 'application/vnd.pawaafile' => 'paw',
- 'application/vnd.pg.format' => 'str',
- 'application/vnd.pg.osasli' => 'ei6',
- 'application/vnd.picsel' => 'efif',
- 'application/vnd.pmi.widget' => 'wg',
- 'application/vnd.pocketlearn' => 'plf',
- 'application/vnd.powerbuilder6' => 'pbd',
- 'application/vnd.previewsystems.box' => 'box',
- 'application/vnd.proteus.magazine' => 'mgz',
- 'application/vnd.publishare-delta-tree' => 'qps',
- 'application/vnd.pvi.ptid1' => 'ptid',
- 'application/vnd.quark.quarkxpress' => 'qxd',
- 'application/vnd.realvnc.bed' => 'bed',
- 'application/vnd.recordare.musicxml' => 'mxl',
- 'application/vnd.recordare.musicxml+xml' => 'musicxml',
- 'application/vnd.rig.cryptonote' => 'cryptonote',
- 'application/vnd.rim.cod' => 'cod',
- 'application/vnd.rn-realmedia' => 'rm',
- 'application/vnd.rn-realmedia-vbr' => 'rmvb',
- 'application/vnd.route66.link66+xml' => 'link66',
- 'application/vnd.sailingtracker.track' => 'st',
- 'application/vnd.seemail' => 'see',
- 'application/vnd.sema' => 'sema',
- 'application/vnd.semd' => 'semd',
- 'application/vnd.semf' => 'semf',
- 'application/vnd.shana.informed.formdata' => 'ifm',
- 'application/vnd.shana.informed.formtemplate' => 'itp',
- 'application/vnd.shana.informed.interchange' => 'iif',
- 'application/vnd.shana.informed.package' => 'ipk',
- 'application/vnd.simtech-mindmapper' => 'twd',
- 'application/vnd.smaf' => 'mmf',
- 'application/vnd.smart.teacher' => 'teacher',
- 'application/vnd.solent.sdkm+xml' => 'sdkm',
- 'application/vnd.spotfire.dxp' => 'dxp',
- 'application/vnd.spotfire.sfs' => 'sfs',
- 'application/vnd.stardivision.calc' => 'sdc',
- 'application/vnd.stardivision.draw' => 'sda',
- 'application/vnd.stardivision.impress' => 'sdd',
- 'application/vnd.stardivision.math' => 'smf',
- 'application/vnd.stardivision.writer' => 'sdw',
- 'application/vnd.stardivision.writer-global' => 'sgl',
- 'application/vnd.stepmania.package' => 'smzip',
- 'application/vnd.stepmania.stepchart' => 'sm',
- 'application/vnd.sun.xml.calc' => 'sxc',
- 'application/vnd.sun.xml.calc.template' => 'stc',
- 'application/vnd.sun.xml.draw' => 'sxd',
- 'application/vnd.sun.xml.draw.template' => 'std',
- 'application/vnd.sun.xml.impress' => 'sxi',
- 'application/vnd.sun.xml.impress.template' => 'sti',
- 'application/vnd.sun.xml.math' => 'sxm',
- 'application/vnd.sun.xml.writer' => 'sxw',
- 'application/vnd.sun.xml.writer.global' => 'sxg',
- 'application/vnd.sun.xml.writer.template' => 'stw',
- 'application/vnd.sus-calendar' => 'sus',
- 'application/vnd.svd' => 'svd',
- 'application/vnd.symbian.install' => 'sis',
- 'application/vnd.syncml+xml' => 'xsm',
- 'application/vnd.syncml.dm+wbxml' => 'bdm',
- 'application/vnd.syncml.dm+xml' => 'xdm',
- 'application/vnd.tao.intent-module-archive' => 'tao',
- 'application/vnd.tcpdump.pcap' => 'pcap',
- 'application/vnd.tmobile-livetv' => 'tmo',
- 'application/vnd.trid.tpt' => 'tpt',
- 'application/vnd.triscape.mxs' => 'mxs',
- 'application/vnd.trueapp' => 'tra',
- 'application/vnd.ufdl' => 'ufd',
- 'application/vnd.uiq.theme' => 'utz',
- 'application/vnd.umajin' => 'umj',
- 'application/vnd.unity' => 'unityweb',
- 'application/vnd.uoml+xml' => 'uoml',
- 'application/vnd.vcx' => 'vcx',
- 'application/vnd.visio' => 'vsd',
- 'application/vnd.visionary' => 'vis',
- 'application/vnd.vsf' => 'vsf',
- 'application/vnd.wap.wbxml' => 'wbxml',
- 'application/vnd.wap.wmlc' => 'wmlc',
- 'application/vnd.wap.wmlscriptc' => 'wmlsc',
- 'application/vnd.webturbo' => 'wtb',
- 'application/vnd.wolfram.player' => 'nbp',
- 'application/vnd.wordperfect' => 'wpd',
- 'application/vnd.wqd' => 'wqd',
- 'application/vnd.wt.stf' => 'stf',
- 'application/vnd.xara' => 'xar',
- 'application/vnd.xfdl' => 'xfdl',
- 'application/vnd.yamaha.hv-dic' => 'hvd',
- 'application/vnd.yamaha.hv-script' => 'hvs',
- 'application/vnd.yamaha.hv-voice' => 'hvp',
- 'application/vnd.yamaha.openscoreformat' => 'osf',
- 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
- 'application/vnd.yamaha.smaf-audio' => 'saf',
- 'application/vnd.yamaha.smaf-phrase' => 'spf',
- 'application/vnd.yellowriver-custom-menu' => 'cmp',
- 'application/vnd.zul' => 'zir',
- 'application/vnd.zzazz.deck+xml' => 'zaz',
- 'application/voicexml+xml' => 'vxml',
- 'application/widget' => 'wgt',
- 'application/winhlp' => 'hlp',
- 'application/wsdl+xml' => 'wsdl',
- 'application/wspolicy+xml' => 'wspolicy',
- 'application/x-7z-compressed' => '7z',
- 'application/x-abiword' => 'abw',
- 'application/x-ace-compressed' => 'ace',
- 'application/x-apple-diskimage' => 'dmg',
- 'application/x-authorware-bin' => 'aab',
- 'application/x-authorware-map' => 'aam',
- 'application/x-authorware-seg' => 'aas',
- 'application/x-bcpio' => 'bcpio',
- 'application/x-bittorrent' => 'torrent',
- 'application/x-blorb' => 'blb',
- 'application/x-bzip' => 'bz',
- 'application/x-bzip2' => 'bz2',
- 'application/x-cbr' => 'cbr',
- 'application/x-cdlink' => 'vcd',
- 'application/x-cfs-compressed' => 'cfs',
- 'application/x-chat' => 'chat',
- 'application/x-chess-pgn' => 'pgn',
- 'application/x-conference' => 'nsc',
- 'application/x-cpio' => 'cpio',
- 'application/x-csh' => 'csh',
- 'application/x-debian-package' => 'deb',
- 'application/x-dgc-compressed' => 'dgc',
- 'application/x-director' => 'dir',
- 'application/x-doom' => 'wad',
- 'application/x-dtbncx+xml' => 'ncx',
- 'application/x-dtbook+xml' => 'dtb',
- 'application/x-dtbresource+xml' => 'res',
- 'application/x-dvi' => 'dvi',
- 'application/x-envoy' => 'evy',
- 'application/x-eva' => 'eva',
- 'application/x-font-bdf' => 'bdf',
- 'application/x-font-ghostscript' => 'gsf',
- 'application/x-font-linux-psf' => 'psf',
- 'application/x-font-otf' => 'otf',
- 'application/x-font-pcf' => 'pcf',
- 'application/x-font-snf' => 'snf',
- 'application/x-font-ttf' => 'ttf',
- 'application/x-font-type1' => 'pfa',
- 'application/x-font-woff' => 'woff',
- 'application/x-freearc' => 'arc',
- 'application/x-futuresplash' => 'spl',
- 'application/x-gca-compressed' => 'gca',
- 'application/x-glulx' => 'ulx',
- 'application/x-gnumeric' => 'gnumeric',
- 'application/x-gramps-xml' => 'gramps',
- 'application/x-gtar' => 'gtar',
- 'application/x-hdf' => 'hdf',
- 'application/x-install-instructions' => 'install',
- 'application/x-iso9660-image' => 'iso',
- 'application/x-java-jnlp-file' => 'jnlp',
- 'application/x-latex' => 'latex',
- 'application/x-lzh-compressed' => 'lzh',
- 'application/x-mie' => 'mie',
- 'application/x-mobipocket-ebook' => 'prc',
- 'application/x-ms-application' => 'application',
- 'application/x-ms-shortcut' => 'lnk',
- 'application/x-ms-wmd' => 'wmd',
- 'application/x-ms-wmz' => 'wmz',
- 'application/x-ms-xbap' => 'xbap',
- 'application/x-msaccess' => 'mdb',
- 'application/x-msbinder' => 'obd',
- 'application/x-mscardfile' => 'crd',
- 'application/x-msclip' => 'clp',
- 'application/x-msdownload' => 'exe',
- 'application/x-msmediaview' => 'mvb',
- 'application/x-msmetafile' => 'wmf',
- 'application/x-msmoney' => 'mny',
- 'application/x-mspublisher' => 'pub',
- 'application/x-msschedule' => 'scd',
- 'application/x-msterminal' => 'trm',
- 'application/x-mswrite' => 'wri',
- 'application/x-netcdf' => 'nc',
- 'application/x-nzb' => 'nzb',
- 'application/x-pkcs12' => 'p12',
- 'application/x-pkcs7-certificates' => 'p7b',
- 'application/x-pkcs7-certreqresp' => 'p7r',
- 'application/x-rar-compressed' => 'rar',
- 'application/x-rar' => 'rar',
- 'application/x-research-info-systems' => 'ris',
- 'application/x-sh' => 'sh',
- 'application/x-shar' => 'shar',
- 'application/x-shockwave-flash' => 'swf',
- 'application/x-silverlight-app' => 'xap',
- 'application/x-sql' => 'sql',
- 'application/x-stuffit' => 'sit',
- 'application/x-stuffitx' => 'sitx',
- 'application/x-subrip' => 'srt',
- 'application/x-sv4cpio' => 'sv4cpio',
- 'application/x-sv4crc' => 'sv4crc',
- 'application/x-t3vm-image' => 't3',
- 'application/x-tads' => 'gam',
- 'application/x-tar' => 'tar',
- 'application/x-tcl' => 'tcl',
- 'application/x-tex' => 'tex',
- 'application/x-tex-tfm' => 'tfm',
- 'application/x-texinfo' => 'texinfo',
- 'application/x-tgif' => 'obj',
- 'application/x-ustar' => 'ustar',
- 'application/x-wais-source' => 'src',
- 'application/x-x509-ca-cert' => 'der',
- 'application/x-xfig' => 'fig',
- 'application/x-xliff+xml' => 'xlf',
- 'application/x-xpinstall' => 'xpi',
- 'application/x-xz' => 'xz',
- 'application/x-zip-compressed' => 'zip',
- 'application/x-zmachine' => 'z1',
- 'application/xaml+xml' => 'xaml',
- 'application/xcap-diff+xml' => 'xdf',
- 'application/xenc+xml' => 'xenc',
- 'application/xhtml+xml' => 'xhtml',
- 'application/xml' => 'xml',
- 'application/xml-dtd' => 'dtd',
- 'application/xop+xml' => 'xop',
- 'application/xproc+xml' => 'xpl',
- 'application/xslt+xml' => 'xslt',
- 'application/xspf+xml' => 'xspf',
- 'application/xv+xml' => 'mxml',
- 'application/yang' => 'yang',
- 'application/yin+xml' => 'yin',
- 'application/zip' => 'zip',
- 'audio/adpcm' => 'adp',
- 'audio/basic' => 'au',
- 'audio/midi' => 'mid',
- 'audio/mp4' => 'm4a',
- 'audio/mpeg' => 'mpga',
- 'audio/ogg' => 'oga',
- 'audio/s3m' => 's3m',
- 'audio/silk' => 'sil',
- 'audio/vnd.dece.audio' => 'uva',
- 'audio/vnd.digital-winds' => 'eol',
- 'audio/vnd.dra' => 'dra',
- 'audio/vnd.dts' => 'dts',
- 'audio/vnd.dts.hd' => 'dtshd',
- 'audio/vnd.lucent.voice' => 'lvp',
- 'audio/vnd.ms-playready.media.pya' => 'pya',
- 'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
- 'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
- 'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
- 'audio/vnd.rip' => 'rip',
- 'audio/webm' => 'weba',
- 'audio/x-aac' => 'aac',
- 'audio/x-aiff' => 'aif',
- 'audio/x-caf' => 'caf',
- 'audio/x-flac' => 'flac',
- 'audio/x-hx-aac-adts' => 'aac',
- 'audio/x-matroska' => 'mka',
- 'audio/x-mpegurl' => 'm3u',
- 'audio/x-ms-wax' => 'wax',
- 'audio/x-ms-wma' => 'wma',
- 'audio/x-pn-realaudio' => 'ram',
- 'audio/x-pn-realaudio-plugin' => 'rmp',
- 'audio/x-wav' => 'wav',
- 'audio/xm' => 'xm',
- 'chemical/x-cdx' => 'cdx',
- 'chemical/x-cif' => 'cif',
- 'chemical/x-cmdf' => 'cmdf',
- 'chemical/x-cml' => 'cml',
- 'chemical/x-csml' => 'csml',
- 'chemical/x-xyz' => 'xyz',
- 'font/collection' => 'ttc',
- 'font/otf' => 'otf',
- 'font/ttf' => 'ttf',
- 'font/woff' => 'woff',
- 'font/woff2' => 'woff2',
- 'image/bmp' => 'bmp',
- 'image/x-ms-bmp' => 'bmp',
- 'image/cgm' => 'cgm',
- 'image/g3fax' => 'g3',
- 'image/gif' => 'gif',
- 'image/ief' => 'ief',
- 'image/jpeg' => 'jpeg',
- 'image/pjpeg' => 'jpeg',
- 'image/ktx' => 'ktx',
- 'image/png' => 'png',
- 'image/prs.btif' => 'btif',
- 'image/sgi' => 'sgi',
- 'image/svg+xml' => 'svg',
- 'image/tiff' => 'tiff',
- 'image/vnd.adobe.photoshop' => 'psd',
- 'image/vnd.dece.graphic' => 'uvi',
- 'image/vnd.djvu' => 'djvu',
- 'image/vnd.dvb.subtitle' => 'sub',
- 'image/vnd.dwg' => 'dwg',
- 'image/vnd.dxf' => 'dxf',
- 'image/vnd.fastbidsheet' => 'fbs',
- 'image/vnd.fpx' => 'fpx',
- 'image/vnd.fst' => 'fst',
- 'image/vnd.fujixerox.edmics-mmr' => 'mmr',
- 'image/vnd.fujixerox.edmics-rlc' => 'rlc',
- 'image/vnd.ms-modi' => 'mdi',
- 'image/vnd.ms-photo' => 'wdp',
- 'image/vnd.net-fpx' => 'npx',
- 'image/vnd.wap.wbmp' => 'wbmp',
- 'image/vnd.xiff' => 'xif',
- 'image/webp' => 'webp',
- 'image/x-3ds' => '3ds',
- 'image/x-cmu-raster' => 'ras',
- 'image/x-cmx' => 'cmx',
- 'image/x-freehand' => 'fh',
- 'image/x-icon' => 'ico',
- 'image/x-mrsid-image' => 'sid',
- 'image/x-pcx' => 'pcx',
- 'image/x-pict' => 'pic',
- 'image/x-portable-anymap' => 'pnm',
- 'image/x-portable-bitmap' => 'pbm',
- 'image/x-portable-graymap' => 'pgm',
- 'image/x-portable-pixmap' => 'ppm',
- 'image/x-rgb' => 'rgb',
- 'image/x-tga' => 'tga',
- 'image/x-xbitmap' => 'xbm',
- 'image/x-xpixmap' => 'xpm',
- 'image/x-xwindowdump' => 'xwd',
- 'message/rfc822' => 'eml',
- 'model/iges' => 'igs',
- 'model/mesh' => 'msh',
- 'model/vnd.collada+xml' => 'dae',
- 'model/vnd.dwf' => 'dwf',
- 'model/vnd.gdl' => 'gdl',
- 'model/vnd.gtw' => 'gtw',
- 'model/vnd.mts' => 'mts',
- 'model/vnd.vtu' => 'vtu',
- 'model/vrml' => 'wrl',
- 'model/x3d+binary' => 'x3db',
- 'model/x3d+vrml' => 'x3dv',
- 'model/x3d+xml' => 'x3d',
- 'text/cache-manifest' => 'appcache',
- 'text/calendar' => 'ics',
- 'text/css' => 'css',
- 'text/csv' => 'csv',
- 'text/html' => 'html',
- 'text/n3' => 'n3',
- 'text/plain' => 'txt',
- 'text/prs.lines.tag' => 'dsc',
- 'text/richtext' => 'rtx',
- 'text/rtf' => 'rtf',
- 'text/sgml' => 'sgml',
- 'text/tab-separated-values' => 'tsv',
- 'text/troff' => 't',
- 'text/turtle' => 'ttl',
- 'text/uri-list' => 'uri',
- 'text/vcard' => 'vcard',
- 'text/vnd.curl' => 'curl',
- 'text/vnd.curl.dcurl' => 'dcurl',
- 'text/vnd.curl.mcurl' => 'mcurl',
- 'text/vnd.curl.scurl' => 'scurl',
- 'text/vnd.dvb.subtitle' => 'sub',
- 'text/vnd.fly' => 'fly',
- 'text/vnd.fmi.flexstor' => 'flx',
- 'text/vnd.graphviz' => 'gv',
- 'text/vnd.in3d.3dml' => '3dml',
- 'text/vnd.in3d.spot' => 'spot',
- 'text/vnd.sun.j2me.app-descriptor' => 'jad',
- 'text/vnd.wap.wml' => 'wml',
- 'text/vnd.wap.wmlscript' => 'wmls',
- 'text/vtt' => 'vtt',
- 'text/x-asm' => 's',
- 'text/x-c' => 'c',
- 'text/x-fortran' => 'f',
- 'text/x-java-source' => 'java',
- 'text/x-nfo' => 'nfo',
- 'text/x-opml' => 'opml',
- 'text/x-pascal' => 'p',
- 'text/x-setext' => 'etx',
- 'text/x-sfv' => 'sfv',
- 'text/x-uuencode' => 'uu',
- 'text/x-vcalendar' => 'vcs',
- 'text/x-vcard' => 'vcf',
- 'video/3gpp' => '3gp',
- 'video/3gpp2' => '3g2',
- 'video/h261' => 'h261',
- 'video/h263' => 'h263',
- 'video/h264' => 'h264',
- 'video/jpeg' => 'jpgv',
- 'video/jpm' => 'jpm',
- 'video/mj2' => 'mj2',
- 'video/mp4' => 'mp4',
- 'video/mpeg' => 'mpeg',
- 'video/ogg' => 'ogv',
- 'video/quicktime' => 'qt',
- 'video/vnd.dece.hd' => 'uvh',
- 'video/vnd.dece.mobile' => 'uvm',
- 'video/vnd.dece.pd' => 'uvp',
- 'video/vnd.dece.sd' => 'uvs',
- 'video/vnd.dece.video' => 'uvv',
- 'video/vnd.dvb.file' => 'dvb',
- 'video/vnd.fvt' => 'fvt',
- 'video/vnd.mpegurl' => 'mxu',
- 'video/vnd.ms-playready.media.pyv' => 'pyv',
- 'video/vnd.uvvu.mp4' => 'uvu',
- 'video/vnd.vivo' => 'viv',
- 'video/webm' => 'webm',
- 'video/x-f4v' => 'f4v',
- 'video/x-fli' => 'fli',
- 'video/x-flv' => 'flv',
- 'video/x-m4v' => 'm4v',
- 'video/x-matroska' => 'mkv',
- 'video/x-mng' => 'mng',
- 'video/x-ms-asf' => 'asf',
- 'video/x-ms-vob' => 'vob',
- 'video/x-ms-wm' => 'wm',
- 'video/x-ms-wmv' => 'wmv',
- 'video/x-ms-wmx' => 'wmx',
- 'video/x-ms-wvx' => 'wvx',
- 'video/x-msvideo' => 'avi',
- 'video/x-sgi-movie' => 'movie',
- 'video/x-smv' => 'smv',
- 'x-conference/x-cooltalk' => 'ice',
- ];
-
- /**
- * {@inheritdoc}
- */
- public function guess($mimeType)
- {
- if (isset($this->defaultExtensions[$mimeType])) {
- return $this->defaultExtensions[$mimeType];
- }
-
- $lcMimeType = strtolower($mimeType);
-
- return isset($this->defaultExtensions[$lcMimeType]) ? $this->defaultExtensions[$lcMimeType] : null;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
deleted file mode 100644
index ece2109caee08..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
+++ /dev/null
@@ -1,138 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
-use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
-use Symfony\Component\Mime\MimeTypes;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
-
-/**
- * A singleton mime type guesser.
- *
- * By default, all mime type guessers provided by the framework are installed
- * (if available on the current OS/PHP setup).
- *
- * You can register custom guessers by calling the register() method on the
- * singleton instance. Custom guessers are always called before any default ones.
- *
- * $guesser = MimeTypeGuesser::getInstance();
- * $guesser->register(new MyCustomMimeTypeGuesser());
- *
- * If you want to change the order of the default guessers, just re-register your
- * preferred one as a custom one. The last registered guesser is preferred over
- * previously registered ones.
- *
- * Re-registering a built-in guesser also allows you to configure it:
- *
- * $guesser = MimeTypeGuesser::getInstance();
- * $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
- *
- * @author Bernhard Schussek
- */
-class MimeTypeGuesser implements MimeTypeGuesserInterface
-{
- /**
- * The singleton instance.
- *
- * @var MimeTypeGuesser
- */
- private static $instance = null;
-
- /**
- * All registered MimeTypeGuesserInterface instances.
- *
- * @var array
- */
- protected $guessers = [];
-
- /**
- * Returns the singleton instance.
- *
- * @return self
- */
- public static function getInstance()
- {
- if (null === self::$instance) {
- self::$instance = new self();
- }
-
- return self::$instance;
- }
-
- /**
- * Resets the singleton instance.
- */
- public static function reset()
- {
- self::$instance = null;
- }
-
- /**
- * Registers all natively provided mime type guessers.
- */
- private function __construct()
- {
- $this->register(new FileBinaryMimeTypeGuesser());
- $this->register(new FileinfoMimeTypeGuesser());
- }
-
- /**
- * Registers a new mime type guesser.
- *
- * When guessing, this guesser is preferred over previously registered ones.
- */
- public function register(MimeTypeGuesserInterface $guesser)
- {
- array_unshift($this->guessers, $guesser);
- }
-
- /**
- * Tries to guess the mime type of the given file.
- *
- * The file is passed to each registered mime type guesser in reverse order
- * of their registration (last registered is queried first). Once a guesser
- * returns a value that is not NULL, this method terminates and returns the
- * value.
- *
- * @param string $path The path to the file
- *
- * @return string The mime type or NULL, if none could be guessed
- *
- * @throws \LogicException
- * @throws FileNotFoundException
- * @throws AccessDeniedException
- */
- public function guess($path)
- {
- if (!is_file($path)) {
- throw new FileNotFoundException($path);
- }
-
- if (!is_readable($path)) {
- throw new AccessDeniedException($path);
- }
-
- foreach ($this->guessers as $guesser) {
- if (null !== $mimeType = $guesser->guess($path)) {
- return $mimeType;
- }
- }
-
- if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) {
- throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
- }
-
- return null;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php
deleted file mode 100644
index eab444890efe1..0000000000000
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php
+++ /dev/null
@@ -1,38 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\File\MimeType;
-
-use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
-use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
-use Symfony\Component\Mime\MimeTypesInterface;
-
-/**
- * Guesses the mime type of a file.
- *
- * @author Bernhard Schussek
- *
- * @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
- */
-interface MimeTypeGuesserInterface
-{
- /**
- * Guesses the mime type of the file with the given path.
- *
- * @param string $path The path to the file
- *
- * @return string|null The mime type or NULL, if none could be guessed
- *
- * @throws FileNotFoundException If the file does not exist
- * @throws AccessDeniedException If the file could not be read
- */
- public function guess($path);
-}
diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php
index 0c67f89077d52..90ee4b3dafb8c 100644
--- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php
+++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php
@@ -60,17 +60,10 @@ class UploadedFile extends File
* @throws FileException If file_uploads is disabled
* @throws FileNotFoundException If the file does not exist
*/
- public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false)
+ public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
{
$this->originalName = $this->getName($originalName);
$this->mimeType = $mimeType ?: 'application/octet-stream';
-
- if (4 < \func_num_args() ? !\is_bool($test) : null !== $error && @filesize($path) === $error) {
- @trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED);
- $error = $test;
- $test = 5 < \func_num_args() ? func_get_arg(5) : false;
- }
-
$this->error = $error ?: UPLOAD_ERR_OK;
$this->test = $test;
@@ -140,24 +133,11 @@ public function getClientMimeType()
*/
public function guessClientExtension()
{
- return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
- }
-
- /**
- * Returns the file size.
- *
- * It is extracted from the request from which the file has been uploaded.
- * Then it should not be considered as a safe value.
- *
- * @deprecated since Symfony 4.1, use getSize() instead.
- *
- * @return int|null The file sizes
- */
- public function getClientSize()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED);
+ if (!class_exists(MimeTypes::class)) {
+ throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
+ }
- return $this->getSize();
+ return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
}
/**
@@ -188,14 +168,11 @@ public function isValid()
/**
* Moves the file to a new location.
*
- * @param string $directory The destination folder
- * @param string $name The new file name
- *
* @return File A File object representing the new file
*
* @throws FileException if, for any reason, the file could not have been moved
*/
- public function move($directory, $name = null)
+ public function move(string $directory, string $name = null)
{
if ($this->isValid()) {
if ($this->test) {
diff --git a/src/Symfony/Component/HttpFoundation/FileBag.php b/src/Symfony/Component/HttpFoundation/FileBag.php
index d79075c920b47..5edd372b62971 100644
--- a/src/Symfony/Component/HttpFoundation/FileBag.php
+++ b/src/Symfony/Component/HttpFoundation/FileBag.php
@@ -43,7 +43,7 @@ public function replace(array $files = [])
/**
* {@inheritdoc}
*/
- public function set($key, $value)
+ public function set(string $key, $value)
{
if (!\is_array($value) && !$value instanceof UploadedFile) {
throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
diff --git a/src/Symfony/Component/HttpFoundation/HeaderBag.php b/src/Symfony/Component/HttpFoundation/HeaderBag.php
index 9ffe6f4fe3b5c..9c7273a734477 100644
--- a/src/Symfony/Component/HttpFoundation/HeaderBag.php
+++ b/src/Symfony/Component/HttpFoundation/HeaderBag.php
@@ -62,9 +62,9 @@ public function __toString()
*
* @return array An array of headers
*/
- public function all(/*string $key = null*/)
+ public function all(string $key = null)
{
- if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
+ if (null !== $key) {
return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
}
@@ -103,21 +103,11 @@ public function add(array $headers)
/**
* Returns a header value by name.
*
- * @param string $key The header name
- * @param string|null $default The default value
- *
* @return string|null The first header value or default value
*/
- public function get($key, $default = null)
+ public function get(string $key, string $default = null)
{
- $headers = $this->all((string) $key);
- if (2 < \func_num_args()) {
- @trigger_error(sprintf('Passing a third argument to "%s()" is deprecated since Symfony 4.4, use method "all()" instead', __METHOD__), E_USER_DEPRECATED);
-
- if (!func_get_arg(2)) {
- return $headers;
- }
- }
+ $headers = $this->all($key);
if (!$headers) {
return $default;
@@ -133,11 +123,10 @@ public function get($key, $default = null)
/**
* Sets a header by name.
*
- * @param string $key The key
* @param string|string[] $values The value or an array of values
* @param bool $replace Whether to replace the actual value or not (true by default)
*/
- public function set($key, $values, $replace = true)
+ public function set(string $key, $values, bool $replace = true)
{
$key = strtr($key, self::UPPER, self::LOWER);
@@ -165,11 +154,9 @@ public function set($key, $values, $replace = true)
/**
* Returns true if the HTTP header is defined.
*
- * @param string $key The HTTP header
- *
* @return bool true if the parameter exists, false otherwise
*/
- public function has($key)
+ public function has(string $key)
{
return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all());
}
@@ -177,22 +164,17 @@ public function has($key)
/**
* Returns true if the given HTTP header contains the given value.
*
- * @param string $key The HTTP header name
- * @param string $value The HTTP value
- *
* @return bool true if the value is contained in the header, false otherwise
*/
- public function contains($key, $value)
+ public function contains(string $key, string $value)
{
- return \in_array($value, $this->all((string) $key));
+ return \in_array($value, $this->all($key));
}
/**
* Removes a header.
- *
- * @param string $key The HTTP header name
*/
- public function remove($key)
+ public function remove(string $key)
{
$key = strtr($key, self::UPPER, self::LOWER);
@@ -206,13 +188,11 @@ public function remove($key)
/**
* Returns the HTTP header value converted to a date.
*
- * @param string $key The parameter key
- *
* @return \DateTimeInterface|null The parsed DateTime or the default value if the header does not exist
*
* @throws \RuntimeException When the HTTP header is not parseable
*/
- public function getDate($key, \DateTime $default = null)
+ public function getDate(string $key, \DateTime $default = null)
{
if (null === $value = $this->get($key)) {
return $default;
@@ -228,10 +208,9 @@ public function getDate($key, \DateTime $default = null)
/**
* Adds a custom Cache-Control directive.
*
- * @param string $key The Cache-Control directive name
- * @param mixed $value The Cache-Control directive value
+ * @param mixed $value The Cache-Control directive value
*/
- public function addCacheControlDirective($key, $value = true)
+ public function addCacheControlDirective(string $key, $value = true)
{
$this->cacheControl[$key] = $value;
@@ -241,11 +220,9 @@ public function addCacheControlDirective($key, $value = true)
/**
* Returns true if the Cache-Control directive is defined.
*
- * @param string $key The Cache-Control directive
- *
* @return bool true if the directive exists, false otherwise
*/
- public function hasCacheControlDirective($key)
+ public function hasCacheControlDirective(string $key)
{
return \array_key_exists($key, $this->cacheControl);
}
@@ -253,21 +230,17 @@ public function hasCacheControlDirective($key)
/**
* Returns a Cache-Control directive value by name.
*
- * @param string $key The directive name
- *
* @return mixed|null The directive value if defined, null otherwise
*/
- public function getCacheControlDirective($key)
+ public function getCacheControlDirective(string $key)
{
return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
}
/**
* Removes a Cache-Control directive.
- *
- * @param string $key The Cache-Control directive
*/
- public function removeCacheControlDirective($key)
+ public function removeCacheControlDirective(string $key)
{
unset($this->cacheControl[$key]);
@@ -304,11 +277,9 @@ protected function getCacheControlHeader()
/**
* Parses a Cache-Control HTTP header.
*
- * @param string $header The value of the Cache-Control HTTP header
- *
* @return array An array representing the attribute values
*/
- protected function parseCacheControl($header)
+ protected function parseCacheControl(string $header)
{
$parts = HeaderUtils::split($header, ',=');
diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php
index 72c53a4710815..80c5a950c9328 100644
--- a/src/Symfony/Component/HttpFoundation/IpUtils.php
+++ b/src/Symfony/Component/HttpFoundation/IpUtils.php
@@ -30,12 +30,11 @@ private function __construct()
/**
* Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
*
- * @param string $requestIp IP to check
- * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
+ * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
*
* @return bool Whether the IP is valid
*/
- public static function checkIp($requestIp, $ips)
+ public static function checkIp(?string $requestIp, $ips)
{
if (!\is_array($ips)) {
$ips = [$ips];
@@ -56,12 +55,11 @@ public static function checkIp($requestIp, $ips)
* Compares two IPv4 addresses.
* In case a subnet is given, it checks if it contains the request IP.
*
- * @param string $requestIp IPv4 address to check
- * @param string $ip IPv4 address or subnet in CIDR notation
+ * @param string $ip IPv4 address or subnet in CIDR notation
*
* @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
*/
- public static function checkIp4($requestIp, $ip)
+ public static function checkIp4(?string $requestIp, string $ip)
{
$cacheKey = $requestIp.'-'.$ip;
if (isset(self::$checkedIps[$cacheKey])) {
@@ -102,14 +100,13 @@ public static function checkIp4($requestIp, $ip)
*
* @see https://github.com/dsp/v6tools
*
- * @param string $requestIp IPv6 address to check
- * @param string $ip IPv6 address or subnet in CIDR notation
+ * @param string $ip IPv6 address or subnet in CIDR notation
*
* @return bool Whether the IP is valid
*
* @throws \RuntimeException When IPV6 support is not enabled
*/
- public static function checkIp6($requestIp, $ip)
+ public static function checkIp6(?string $requestIp, string $ip)
{
$cacheKey = $requestIp.'-'.$ip;
if (isset(self::$checkedIps[$cacheKey])) {
diff --git a/src/Symfony/Component/HttpFoundation/JsonResponse.php b/src/Symfony/Component/HttpFoundation/JsonResponse.php
index 11a0bebf88302..b361d0852d50b 100644
--- a/src/Symfony/Component/HttpFoundation/JsonResponse.php
+++ b/src/Symfony/Component/HttpFoundation/JsonResponse.php
@@ -63,9 +63,13 @@ public function __construct($data = null, int $status = 200, array $headers = []
* @param array $headers An array of response headers
*
* @return static
+ *
+ * @deprecated since Symfony 5.1, use __construct() instead.
*/
- public static function create($data = null, $status = 200, $headers = [])
+ public static function create($data = null, int $status = 200, array $headers = [])
{
+ trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, \get_called_class());
+
return new static($data, $status, $headers);
}
@@ -83,7 +87,7 @@ public static function create($data = null, $status = 200, $headers = [])
*
* @return static
*/
- public static function fromJsonString($data = null, $status = 200, $headers = [])
+ public static function fromJsonString(string $data = null, int $status = 200, array $headers = [])
{
return new static($data, $status, $headers, true);
}
@@ -97,7 +101,7 @@ public static function fromJsonString($data = null, $status = 200, $headers = []
*
* @throws \InvalidArgumentException When the callback name is not valid
*/
- public function setCallback($callback = null)
+ public function setCallback(string $callback = null)
{
if (null !== $callback) {
// partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/
@@ -126,13 +130,11 @@ public function setCallback($callback = null)
/**
* Sets a raw string containing a JSON document to be sent.
*
- * @param string $json
- *
* @return $this
*
* @throws \InvalidArgumentException
*/
- public function setJson($json)
+ public function setJson(string $json)
{
$this->data = $json;
@@ -183,13 +185,11 @@ public function getEncodingOptions()
/**
* Sets options used while encoding data to JSON.
*
- * @param int $encodingOptions
- *
* @return $this
*/
- public function setEncodingOptions($encodingOptions)
+ public function setEncodingOptions(int $encodingOptions)
{
- $this->encodingOptions = (int) $encodingOptions;
+ $this->encodingOptions = $encodingOptions;
return $this->setData(json_decode($this->data));
}
diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php
index 20ca6758b68bc..212149c7b99f0 100644
--- a/src/Symfony/Component/HttpFoundation/ParameterBag.php
+++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php
@@ -67,12 +67,11 @@ public function add(array $parameters = [])
/**
* Returns a parameter by name.
*
- * @param string $key The key
- * @param mixed $default The default value if the parameter key does not exist
+ * @param mixed $default The default value if the parameter key does not exist
*
* @return mixed
*/
- public function get($key, $default = null)
+ public function get(string $key, $default = null)
{
return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
}
@@ -80,10 +79,9 @@ public function get($key, $default = null)
/**
* Sets a parameter by name.
*
- * @param string $key The key
- * @param mixed $value The value
+ * @param mixed $value The value
*/
- public function set($key, $value)
+ public function set(string $key, $value)
{
$this->parameters[$key] = $value;
}
@@ -91,21 +89,17 @@ public function set($key, $value)
/**
* Returns true if the parameter is defined.
*
- * @param string $key The key
- *
* @return bool true if the parameter exists, false otherwise
*/
- public function has($key)
+ public function has(string $key)
{
return \array_key_exists($key, $this->parameters);
}
/**
* Removes a parameter.
- *
- * @param string $key The key
*/
- public function remove($key)
+ public function remove(string $key)
{
unset($this->parameters[$key]);
}
@@ -113,12 +107,9 @@ public function remove($key)
/**
* Returns the alphabetic characters of the parameter value.
*
- * @param string $key The parameter key
- * @param string $default The default value if the parameter key does not exist
- *
* @return string The filtered value
*/
- public function getAlpha($key, $default = '')
+ public function getAlpha(string $key, string $default = '')
{
return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
}
@@ -126,12 +117,9 @@ public function getAlpha($key, $default = '')
/**
* Returns the alphabetic characters and digits of the parameter value.
*
- * @param string $key The parameter key
- * @param string $default The default value if the parameter key does not exist
- *
* @return string The filtered value
*/
- public function getAlnum($key, $default = '')
+ public function getAlnum(string $key, string $default = '')
{
return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
}
@@ -139,12 +127,9 @@ public function getAlnum($key, $default = '')
/**
* Returns the digits of the parameter value.
*
- * @param string $key The parameter key
- * @param string $default The default value if the parameter key does not exist
- *
* @return string The filtered value
*/
- public function getDigits($key, $default = '')
+ public function getDigits(string $key, string $default = '')
{
// we need to remove - and + because they're allowed in the filter
return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
@@ -153,12 +138,9 @@ public function getDigits($key, $default = '')
/**
* Returns the parameter value converted to integer.
*
- * @param string $key The parameter key
- * @param int $default The default value if the parameter key does not exist
- *
* @return int The filtered value
*/
- public function getInt($key, $default = 0)
+ public function getInt(string $key, int $default = 0)
{
return (int) $this->get($key, $default);
}
@@ -166,12 +148,9 @@ public function getInt($key, $default = 0)
/**
* Returns the parameter value converted to boolean.
*
- * @param string $key The parameter key
- * @param bool $default The default value if the parameter key does not exist
- *
* @return bool The filtered value
*/
- public function getBoolean($key, $default = false)
+ public function getBoolean(string $key, bool $default = false)
{
return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
}
@@ -179,16 +158,15 @@ public function getBoolean($key, $default = false)
/**
* Filter key.
*
- * @param string $key Key
- * @param mixed $default Default = null
- * @param int $filter FILTER_* constant
- * @param mixed $options Filter options
+ * @param mixed $default Default = null
+ * @param int $filter FILTER_* constant
+ * @param mixed $options Filter options
*
* @see https://php.net/filter-var
*
* @return mixed
*/
- public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = [])
+ public function filter(string $key, $default = null, int $filter = FILTER_DEFAULT, $options = [])
{
$value = $this->get($key, $default);
diff --git a/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/src/Symfony/Component/HttpFoundation/RedirectResponse.php
index 4347f3a844917..0dfb80c039815 100644
--- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php
+++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php
@@ -32,13 +32,8 @@ class RedirectResponse extends Response
*
* @see https://tools.ietf.org/html/rfc2616#section-10.3
*/
- public function __construct(?string $url, int $status = 302, array $headers = [])
+ public function __construct(string $url, int $status = 302, array $headers = [])
{
- if (null === $url) {
- @trigger_error(sprintf('Passing a null url when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED);
- $url = '';
- }
-
parent::__construct('', $status, $headers);
$this->setTargetUrl($url);
@@ -55,14 +50,16 @@ public function __construct(?string $url, int $status = 302, array $headers = []
/**
* Factory method for chainability.
*
- * @param string $url The url to redirect to
- * @param int $status The response status code
- * @param array $headers An array of response headers
+ * @param string $url The URL to redirect to
*
* @return static
+ *
+ * @deprecated since Symfony 5.1, use __construct() instead.
*/
- public static function create($url = '', $status = 302, $headers = [])
+ public static function create($url = '', int $status = 302, array $headers = [])
{
+ trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, \get_called_class());
+
return new static($url, $status, $headers);
}
@@ -79,15 +76,13 @@ public function getTargetUrl()
/**
* Sets the redirect target of this response.
*
- * @param string $url The URL to redirect to
- *
* @return $this
*
* @throws \InvalidArgumentException
*/
- public function setTargetUrl($url)
+ public function setTargetUrl(string $url)
{
- if ('' === ($url ?? '')) {
+ if ('' === $url) {
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
}
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index 6690b9b22165b..847c44b943b10 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -199,6 +199,11 @@ class Request
private $isHostValid = true;
private $isForwardedValid = true;
+ /**
+ * @var bool|null
+ */
+ private $isSafeContentPreferred;
+
private static $trustedHeaderSet = -1;
private static $forwardedParams = [
@@ -310,7 +315,7 @@ public static function createFromGlobals()
*
* @return static
*/
- public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
+ public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
$server = array_replace([
'SERVER_NAME' => 'localhost',
@@ -408,10 +413,8 @@ public static function create($uri, $method = 'GET', $parameters = [], $cookies
* This is mainly useful when you need to override the Request class
* to keep BC with an existing system. It should not be used for any
* other purpose.
- *
- * @param callable|null $callable A PHP callable
*/
- public static function setFactory($callable)
+ public static function setFactory(?callable $callable)
{
self::$requestFactory = $callable;
}
@@ -638,11 +641,9 @@ public static function getTrustedHosts()
* It builds a normalized query string, where keys/value pairs are alphabetized,
* have consistent escaping and unneeded delimiters are removed.
*
- * @param string $qs Query string
- *
* @return string A normalized query string for the Request
*/
- public static function normalizeQueryString($qs)
+ public static function normalizeQueryString(?string $qs)
{
if ('' === ($qs ?? '')) {
return '';
@@ -689,12 +690,11 @@ public static function getHttpMethodParameterOverride()
*
* Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
*
- * @param string $key The key
- * @param mixed $default The default value if the parameter key does not exist
+ * @param mixed $default The default value if the parameter key does not exist
*
* @return mixed
*/
- public function get($key, $default = null)
+ public function get(string $key, $default = null)
{
if ($this !== $result = $this->attributes->get($key, $this)) {
return $result;
@@ -724,8 +724,7 @@ public function getSession()
}
if (null === $session) {
- @trigger_error(sprintf('Calling "%s()" when no session has been set is deprecated since Symfony 4.1 and will throw an exception in 5.0. Use "hasSession()" instead.', __METHOD__), E_USER_DEPRECATED);
- // throw new \BadMethodCallException('Session has not been set');
+ throw new \BadMethodCallException('Session has not been set.');
}
return $session;
@@ -1041,7 +1040,7 @@ public function getUri()
*
* @return string The normalized URI for the path
*/
- public function getUriForPath($path)
+ public function getUriForPath(string $path)
{
return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
}
@@ -1061,11 +1060,9 @@ public function getUriForPath($path)
* - "/a/b/c/other" -> "other"
* - "/a/x/y" -> "../../x/y"
*
- * @param string $path The target path
- *
* @return string The relative target path
*/
- public function getRelativeUriForPath($path)
+ public function getRelativeUriForPath(string $path)
{
// be sure that we are dealing with an absolute path
if (!isset($path[0]) || '/' !== $path[0]) {
@@ -1203,10 +1200,8 @@ public function getHost()
/**
* Sets the request method.
- *
- * @param string $method
*/
- public function setMethod($method)
+ public function setMethod(string $method)
{
$this->method = null;
$this->server->set('REQUEST_METHOD', $method);
@@ -1277,11 +1272,9 @@ public function getRealMethod()
/**
* Gets the mime type associated with the format.
*
- * @param string $format The format
- *
* @return string|null The associated mime type (null if not found)
*/
- public function getMimeType($format)
+ public function getMimeType(string $format)
{
if (null === static::$formats) {
static::initializeFormats();
@@ -1293,11 +1286,9 @@ public function getMimeType($format)
/**
* Gets the mime types associated with the format.
*
- * @param string $format The format
- *
* @return array The associated mime types
*/
- public static function getMimeTypes($format)
+ public static function getMimeTypes(string $format)
{
if (null === static::$formats) {
static::initializeFormats();
@@ -1309,11 +1300,9 @@ public static function getMimeTypes($format)
/**
* Gets the format associated with the mime type.
*
- * @param string $mimeType The associated mime type
- *
* @return string|null The format (null if not found)
*/
- public function getFormat($mimeType)
+ public function getFormat(?string $mimeType)
{
$canonicalMimeType = null;
if (false !== $pos = strpos($mimeType, ';')) {
@@ -1339,10 +1328,9 @@ public function getFormat($mimeType)
/**
* Associates a format with mime types.
*
- * @param string $format The format
* @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
*/
- public function setFormat($format, $mimeTypes)
+ public function setFormat(?string $format, $mimeTypes)
{
if (null === static::$formats) {
static::initializeFormats();
@@ -1362,11 +1350,9 @@ public function setFormat($format, $mimeTypes)
*
* @see getPreferredFormat
*
- * @param string|null $default The default format
- *
* @return string|null The request format
*/
- public function getRequestFormat($default = 'html')
+ public function getRequestFormat(?string $default = 'html')
{
if (null === $this->format) {
$this->format = $this->attributes->get('_format');
@@ -1377,10 +1363,8 @@ public function getRequestFormat($default = 'html')
/**
* Sets the request format.
- *
- * @param string $format The request format
*/
- public function setRequestFormat($format)
+ public function setRequestFormat(?string $format)
{
$this->format = $format;
}
@@ -1397,10 +1381,8 @@ public function getContentType()
/**
* Sets the default locale.
- *
- * @param string $locale
*/
- public function setDefaultLocale($locale)
+ public function setDefaultLocale(string $locale)
{
$this->defaultLocale = $locale;
@@ -1421,10 +1403,8 @@ public function getDefaultLocale()
/**
* Sets the locale.
- *
- * @param string $locale
*/
- public function setLocale($locale)
+ public function setLocale(string $locale)
{
$this->setPhpDefaultLocale($this->locale = $locale);
}
@@ -1446,7 +1426,7 @@ public function getLocale()
*
* @return bool
*/
- public function isMethod($method)
+ public function isMethod(string $method)
{
return $this->getMethod() === strtoupper($method);
}
@@ -1460,10 +1440,6 @@ public function isMethod($method)
*/
public function isMethodSafe()
{
- if (\func_num_args() > 0) {
- @trigger_error(sprintf('Passing arguments to "%s()" has been deprecated since Symfony 4.4; use "%s::isMethodCacheable()" to check if the method is cacheable instead.', __METHOD__, __CLASS__), E_USER_DEPRECATED);
- }
-
return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
}
@@ -1522,7 +1498,7 @@ public function getProtocolVersion()
*
* @throws \LogicException
*/
- public function getContent($asResource = false)
+ public function getContent(bool $asResource = false)
{
$currentContentIsResource = \is_resource($this->content);
@@ -1731,6 +1707,29 @@ public function isXmlHttpRequest()
return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
}
+ /**
+ * Checks whether the client browser prefers safe content or not according to RFC8674.
+ *
+ * @see https://tools.ietf.org/html/rfc8674
+ */
+ public function preferSafeContent(): bool
+ {
+ if (null !== $this->isSafeContentPreferred) {
+ return $this->isSafeContentPreferred;
+ }
+
+ if (!$this->isSecure()) {
+ // see https://tools.ietf.org/html/rfc8674#section-3
+ $this->isSafeContentPreferred = false;
+
+ return $this->isSafeContentPreferred;
+ }
+
+ $this->isSafeContentPreferred = AcceptHeader::fromString($this->headers->get('Prefer'))->has('safe');
+
+ return $this->isSafeContentPreferred;
+ }
+
/*
* The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
*
diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/src/Symfony/Component/HttpFoundation/RequestMatcher.php
index 9a4a2a13761e1..c32c5cdce5e1c 100644
--- a/src/Symfony/Component/HttpFoundation/RequestMatcher.php
+++ b/src/Symfony/Component/HttpFoundation/RequestMatcher.php
@@ -84,10 +84,8 @@ public function matchScheme($scheme)
/**
* Adds a check for the URL host name.
- *
- * @param string|null $regexp A Regexp
*/
- public function matchHost($regexp)
+ public function matchHost(?string $regexp)
{
$this->host = $regexp;
}
@@ -104,10 +102,8 @@ public function matchPort(?int $port)
/**
* Adds a check for the URL path info.
- *
- * @param string|null $regexp A Regexp
*/
- public function matchPath($regexp)
+ public function matchPath(?string $regexp)
{
$this->path = $regexp;
}
@@ -117,7 +113,7 @@ public function matchPath($regexp)
*
* @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
*/
- public function matchIp($ip)
+ public function matchIp(string $ip)
{
$this->matchIps($ip);
}
@@ -144,11 +140,8 @@ public function matchMethod($method)
/**
* Adds a check for request attribute.
- *
- * @param string $key The request attribute name
- * @param string $regexp A Regexp
*/
- public function matchAttribute($key, $regexp)
+ public function matchAttribute(string $key, string $regexp)
{
$this->attributes[$key] = $regexp;
}
diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php
index b267cd8ceb57b..b09763426857b 100644
--- a/src/Symfony/Component/HttpFoundation/Response.php
+++ b/src/Symfony/Component/HttpFoundation/Response.php
@@ -64,11 +64,6 @@ class Response
const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
const HTTP_LOCKED = 423; // RFC4918
const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
-
- /**
- * @deprecated
- */
- const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
@@ -196,7 +191,7 @@ class Response
/**
* @throws \InvalidArgumentException When the HTTP status code is not valid
*/
- public function __construct($content = '', int $status = 200, array $headers = [])
+ public function __construct(?string $content = '', int $status = 200, array $headers = [])
{
$this->headers = new ResponseHeaderBag($headers);
$this->setContent($content);
@@ -212,14 +207,14 @@ public function __construct($content = '', int $status = 200, array $headers = [
* return Response::create($body, 200)
* ->setSharedMaxAge(300);
*
- * @param mixed $content The response content, see setContent()
- * @param int $status The response status code
- * @param array $headers An array of response headers
- *
* @return static
+ *
+ * @deprecated since Symfony 5.1, use __construct() instead.
*/
- public static function create($content = '', $status = 200, $headers = [])
+ public static function create(?string $content = '', int $status = 200, array $headers = [])
{
+ trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, \get_called_class());
+
return new static($content, $status, $headers);
}
@@ -389,21 +384,13 @@ public function send()
/**
* Sets the response content.
*
- * Valid types are strings, numbers, null, and objects that implement a __toString() method.
- *
- * @param mixed $content Content that can be cast to string
- *
* @return $this
*
* @throws \UnexpectedValueException
*/
- public function setContent($content)
+ public function setContent(?string $content)
{
- if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) {
- throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content)));
- }
-
- $this->content = (string) $content;
+ $this->content = $content ?? '';
return $this;
}
@@ -425,7 +412,7 @@ public function getContent()
*
* @final
*/
- public function setProtocolVersion(string $version)
+ public function setProtocolVersion(string $version): object
{
$this->version = $version;
@@ -454,7 +441,7 @@ public function getProtocolVersion(): string
*
* @final
*/
- public function setStatusCode(int $code, $text = null)
+ public function setStatusCode(int $code, $text = null): object
{
$this->statusCode = $code;
if ($this->isInvalid()) {
@@ -495,7 +482,7 @@ public function getStatusCode(): int
*
* @final
*/
- public function setCharset(string $charset)
+ public function setCharset(string $charset): object
{
$this->charset = $charset;
@@ -576,7 +563,7 @@ public function isValidateable(): bool
*
* @final
*/
- public function setPrivate()
+ public function setPrivate(): object
{
$this->headers->removeCacheControlDirective('public');
$this->headers->addCacheControlDirective('private');
@@ -593,7 +580,7 @@ public function setPrivate()
*
* @final
*/
- public function setPublic()
+ public function setPublic(): object
{
$this->headers->addCacheControlDirective('public');
$this->headers->removeCacheControlDirective('private');
@@ -608,7 +595,7 @@ public function setPublic()
*
* @final
*/
- public function setImmutable(bool $immutable = true)
+ public function setImmutable(bool $immutable = true): object
{
if ($immutable) {
$this->headers->addCacheControlDirective('immutable');
@@ -663,7 +650,7 @@ public function getDate(): ?\DateTimeInterface
*
* @final
*/
- public function setDate(\DateTimeInterface $date)
+ public function setDate(\DateTimeInterface $date): object
{
if ($date instanceof \DateTime) {
$date = \DateTimeImmutable::createFromMutable($date);
@@ -728,7 +715,7 @@ public function getExpires(): ?\DateTimeInterface
*
* @final
*/
- public function setExpires(\DateTimeInterface $date = null)
+ public function setExpires(\DateTimeInterface $date = null): object
{
if (null === $date) {
$this->headers->remove('Expires');
@@ -781,7 +768,7 @@ public function getMaxAge(): ?int
*
* @final
*/
- public function setMaxAge(int $value)
+ public function setMaxAge(int $value): object
{
$this->headers->addCacheControlDirective('max-age', $value);
@@ -797,7 +784,7 @@ public function setMaxAge(int $value)
*
* @final
*/
- public function setSharedMaxAge(int $value)
+ public function setSharedMaxAge(int $value): object
{
$this->setPublic();
$this->headers->addCacheControlDirective('s-maxage', $value);
@@ -831,7 +818,7 @@ public function getTtl(): ?int
*
* @final
*/
- public function setTtl(int $seconds)
+ public function setTtl(int $seconds): object
{
$this->setSharedMaxAge($this->getAge() + $seconds);
@@ -847,7 +834,7 @@ public function setTtl(int $seconds)
*
* @final
*/
- public function setClientTtl(int $seconds)
+ public function setClientTtl(int $seconds): object
{
$this->setMaxAge($this->getAge() + $seconds);
@@ -875,7 +862,7 @@ public function getLastModified(): ?\DateTimeInterface
*
* @final
*/
- public function setLastModified(\DateTimeInterface $date = null)
+ public function setLastModified(\DateTimeInterface $date = null): object
{
if (null === $date) {
$this->headers->remove('Last-Modified');
@@ -913,7 +900,7 @@ public function getEtag(): ?string
*
* @final
*/
- public function setEtag(string $etag = null, bool $weak = false)
+ public function setEtag(string $etag = null, bool $weak = false): object
{
if (null === $etag) {
$this->headers->remove('Etag');
@@ -939,7 +926,7 @@ public function setEtag(string $etag = null, bool $weak = false)
*
* @final
*/
- public function setCache(array $options)
+ public function setCache(array $options): object
{
if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
@@ -996,7 +983,7 @@ public function setCache(array $options)
*
* @final
*/
- public function setNotModified()
+ public function setNotModified(): object
{
$this->setStatusCode(304);
$this->setContent(null);
@@ -1048,7 +1035,7 @@ public function getVary(): array
*
* @final
*/
- public function setVary($headers, bool $replace = true)
+ public function setVary($headers, bool $replace = true): object
{
$this->headers->set('Vary', $headers, $replace);
@@ -1225,6 +1212,22 @@ public static function closeOutputBuffers(int $targetLevel, bool $flush): void
}
}
+ /**
+ * Marks a response as safe according to RFC8674.
+ *
+ * @see https://tools.ietf.org/html/rfc8674
+ */
+ public function setContentSafe(bool $safe = true): void
+ {
+ if ($safe) {
+ $this->headers->set('Preference-Applied', 'safe');
+ } elseif ('safe' === $this->headers->get('Preference-Applied')) {
+ $this->headers->remove('Preference-Applied');
+ }
+
+ $this->setVary('Prefer', false);
+ }
+
/**
* Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
*
diff --git a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
index cdc66990488d0..56b9245f93587 100644
--- a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
+++ b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
@@ -87,14 +87,12 @@ public function replace(array $headers = [])
/**
* {@inheritdoc}
- *
- * @param string|null $key The name of the headers to return or null to get them all
*/
- public function all(/*string $key = null*/)
+ public function all(string $key = null)
{
$headers = parent::all();
- if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
+ if (null !== $key) {
$key = strtr($key, self::UPPER, self::LOWER);
return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies());
@@ -110,7 +108,7 @@ public function all(/*string $key = null*/)
/**
* {@inheritdoc}
*/
- public function set($key, $values, $replace = true)
+ public function set(string $key, $values, bool $replace = true)
{
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
@@ -141,7 +139,7 @@ public function set($key, $values, $replace = true)
/**
* {@inheritdoc}
*/
- public function remove($key)
+ public function remove(string $key)
{
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
unset($this->headerNames[$uniqueKey]);
@@ -166,7 +164,7 @@ public function remove($key)
/**
* {@inheritdoc}
*/
- public function hasCacheControlDirective($key)
+ public function hasCacheControlDirective(string $key)
{
return \array_key_exists($key, $this->computedCacheControl);
}
@@ -174,7 +172,7 @@ public function hasCacheControlDirective($key)
/**
* {@inheritdoc}
*/
- public function getCacheControlDirective($key)
+ public function getCacheControlDirective(string $key)
{
return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
}
@@ -187,12 +185,8 @@ public function setCookie(Cookie $cookie)
/**
* Removes a cookie from the array, but does not unset it in the browser.
- *
- * @param string $name
- * @param string $path
- * @param string $domain
*/
- public function removeCookie($name, $path = '/', $domain = null)
+ public function removeCookie(string $name, ?string $path = '/', string $domain = null)
{
if (null === $path) {
$path = '/';
@@ -216,13 +210,11 @@ public function removeCookie($name, $path = '/', $domain = null)
/**
* Returns an array with all cookies.
*
- * @param string $format
- *
* @return Cookie[]
*
* @throws \InvalidArgumentException When the $format is invalid
*/
- public function getCookies($format = self::COOKIES_FLAT)
+ public function getCookies(string $format = self::COOKIES_FLAT)
{
if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
@@ -246,14 +238,8 @@ public function getCookies($format = self::COOKIES_FLAT)
/**
* Clears a cookie in the browser.
- *
- * @param string $name
- * @param string $path
- * @param string $domain
- * @param bool $secure
- * @param bool $httpOnly
*/
- public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
+ public function clearCookie(string $name, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
}
@@ -261,9 +247,9 @@ public function clearCookie($name, $path = '/', $domain = null, $secure = false,
/**
* @see HeaderUtils::makeDisposition()
*/
- public function makeDisposition($disposition, $filename, $filenameFallback = '')
+ public function makeDisposition(string $disposition, string $filename, string $filenameFallback = '')
{
- return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
+ return HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback);
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
index ee33698cf0842..aad6b610e7bb3 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
@@ -37,7 +37,7 @@ public function getName()
return $this->name;
}
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -61,7 +61,7 @@ public function getStorageKey()
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
return \array_key_exists($name, $this->attributes);
}
@@ -69,7 +69,7 @@ public function has($name)
/**
* {@inheritdoc}
*/
- public function get($name, $default = null)
+ public function get(string $name, $default = null)
{
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
@@ -77,7 +77,7 @@ public function get($name, $default = null)
/**
* {@inheritdoc}
*/
- public function set($name, $value)
+ public function set(string $name, $value)
{
$this->attributes[$name] = $value;
}
@@ -104,7 +104,7 @@ public function replace(array $attributes)
/**
* {@inheritdoc}
*/
- public function remove($name)
+ public function remove(string $name)
{
$retval = null;
if (\array_key_exists($name, $this->attributes)) {
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php
index 6fa2293970b90..7017b717e4064 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php
@@ -23,29 +23,25 @@ interface AttributeBagInterface extends SessionBagInterface
/**
* Checks if an attribute is defined.
*
- * @param string $name The attribute name
- *
* @return bool true if the attribute is defined, false otherwise
*/
- public function has($name);
+ public function has(string $name);
/**
* Returns an attribute.
*
- * @param string $name The attribute name
- * @param mixed $default The default value if not found
+ * @param mixed $default The default value if not found
*
* @return mixed
*/
- public function get($name, $default = null);
+ public function get(string $name, $default = null);
/**
* Sets an attribute.
*
- * @param string $name
- * @param mixed $value
+ * @param mixed $value
*/
- public function set($name, $value);
+ public function set(string $name, $value);
/**
* Returns attributes.
@@ -59,9 +55,7 @@ public function replace(array $attributes);
/**
* Removes an attribute.
*
- * @param string $name
- *
* @return mixed The removed value or null when it does not exist
*/
- public function remove($name);
+ public function remove(string $name);
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
index 2cf0743cf9d5e..7e752ddaa7ec1 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
@@ -34,7 +34,7 @@ public function __construct(string $storageKey = '_sf2_attributes', string $name
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
// reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
$attributes = $this->resolveAttributePath($name);
@@ -50,7 +50,7 @@ public function has($name)
/**
* {@inheritdoc}
*/
- public function get($name, $default = null)
+ public function get(string $name, $default = null)
{
// reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
$attributes = $this->resolveAttributePath($name);
@@ -66,7 +66,7 @@ public function get($name, $default = null)
/**
* {@inheritdoc}
*/
- public function set($name, $value)
+ public function set(string $name, $value)
{
$attributes = &$this->resolveAttributePath($name, true);
$name = $this->resolveKey($name);
@@ -76,7 +76,7 @@ public function set($name, $value)
/**
* {@inheritdoc}
*/
- public function remove($name)
+ public function remove(string $name)
{
$retval = null;
$attributes = &$this->resolveAttributePath($name);
@@ -99,7 +99,7 @@ public function remove($name)
*
* @return array|null
*/
- protected function &resolveAttributePath($name, $writeContext = false)
+ protected function &resolveAttributePath(string $name, bool $writeContext = false)
{
$array = &$this->attributes;
$name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
@@ -144,11 +144,9 @@ protected function &resolveAttributePath($name, $writeContext = false)
*
* This is the last part in a dot separated string.
*
- * @param string $name
- *
* @return string
*/
- protected function resolveKey($name)
+ protected function resolveKey(string $name)
{
if (false !== $pos = strrpos($name, $this->namespaceCharacter)) {
$name = substr($name, $pos + 1);
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
index 6502f3d50155f..2707d64ec7138 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
@@ -38,7 +38,7 @@ public function getName()
return $this->name;
}
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -60,7 +60,7 @@ public function initialize(array &$flashes)
/**
* {@inheritdoc}
*/
- public function add($type, $message)
+ public function add(string $type, $message)
{
$this->flashes['new'][$type][] = $message;
}
@@ -68,7 +68,7 @@ public function add($type, $message)
/**
* {@inheritdoc}
*/
- public function peek($type, array $default = [])
+ public function peek(string $type, array $default = [])
{
return $this->has($type) ? $this->flashes['display'][$type] : $default;
}
@@ -84,7 +84,7 @@ public function peekAll()
/**
* {@inheritdoc}
*/
- public function get($type, array $default = [])
+ public function get(string $type, array $default = [])
{
$return = $default;
@@ -122,7 +122,7 @@ public function setAll(array $messages)
/**
* {@inheritdoc}
*/
- public function set($type, $messages)
+ public function set(string $type, $messages)
{
$this->flashes['new'][$type] = (array) $messages;
}
@@ -130,7 +130,7 @@ public function set($type, $messages)
/**
* {@inheritdoc}
*/
- public function has($type)
+ public function has(string $type)
{
return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
index c6b7ce354cec4..88df7508ac689 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
@@ -38,7 +38,7 @@ public function getName()
return $this->name;
}
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -54,7 +54,7 @@ public function initialize(array &$flashes)
/**
* {@inheritdoc}
*/
- public function add($type, $message)
+ public function add(string $type, $message)
{
$this->flashes[$type][] = $message;
}
@@ -62,7 +62,7 @@ public function add($type, $message)
/**
* {@inheritdoc}
*/
- public function peek($type, array $default = [])
+ public function peek(string $type, array $default = [])
{
return $this->has($type) ? $this->flashes[$type] : $default;
}
@@ -78,7 +78,7 @@ public function peekAll()
/**
* {@inheritdoc}
*/
- public function get($type, array $default = [])
+ public function get(string $type, array $default = [])
{
if (!$this->has($type)) {
return $default;
@@ -105,7 +105,7 @@ public function all()
/**
* {@inheritdoc}
*/
- public function set($type, $messages)
+ public function set(string $type, $messages)
{
$this->flashes[$type] = (array) $messages;
}
@@ -121,7 +121,7 @@ public function setAll(array $messages)
/**
* {@inheritdoc}
*/
- public function has($type)
+ public function has(string $type)
{
return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php
index 99e8074214b62..8713e71d0f62e 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php
@@ -23,18 +23,16 @@ interface FlashBagInterface extends SessionBagInterface
/**
* Adds a flash message for the given type.
*
- * @param string $type
- * @param mixed $message
+ * @param mixed $message
*/
- public function add($type, $message);
+ public function add(string $type, $message);
/**
* Registers one or more messages for a given type.
*
- * @param string $type
* @param string|array $messages
*/
- public function set($type, $messages);
+ public function set(string $type, $messages);
/**
* Gets flash messages for a given type.
@@ -44,7 +42,7 @@ public function set($type, $messages);
*
* @return array
*/
- public function peek($type, array $default = []);
+ public function peek(string $type, array $default = []);
/**
* Gets all flash messages.
@@ -56,12 +54,11 @@ public function peekAll();
/**
* Gets and clears flash from the stack.
*
- * @param string $type
- * @param array $default Default value if $type does not exist
+ * @param array $default Default value if $type does not exist
*
* @return array
*/
- public function get($type, array $default = []);
+ public function get(string $type, array $default = []);
/**
* Gets and clears flashes from the stack.
@@ -78,11 +75,9 @@ public function setAll(array $messages);
/**
* Has flash messages for a given type?
*
- * @param string $type
- *
* @return bool
*/
- public function has($type);
+ public function has(string $type);
/**
* Returns a list of all defined types.
diff --git a/src/Symfony/Component/HttpFoundation/Session/Session.php b/src/Symfony/Component/HttpFoundation/Session/Session.php
index 2192c629e8963..89071b861ef1e 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Session.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Session.php
@@ -55,7 +55,7 @@ public function start()
/**
* {@inheritdoc}
*/
- public function has($name)
+ public function has(string $name)
{
return $this->getAttributeBag()->has($name);
}
@@ -63,7 +63,7 @@ public function has($name)
/**
* {@inheritdoc}
*/
- public function get($name, $default = null)
+ public function get(string $name, $default = null)
{
return $this->getAttributeBag()->get($name, $default);
}
@@ -71,7 +71,7 @@ public function get($name, $default = null)
/**
* {@inheritdoc}
*/
- public function set($name, $value)
+ public function set(string $name, $value)
{
$this->getAttributeBag()->set($name, $value);
}
@@ -95,7 +95,7 @@ public function replace(array $attributes)
/**
* {@inheritdoc}
*/
- public function remove($name)
+ public function remove(string $name)
{
return $this->getAttributeBag()->remove($name);
}
@@ -161,7 +161,7 @@ public function isEmpty(): bool
/**
* {@inheritdoc}
*/
- public function invalidate($lifetime = null)
+ public function invalidate(int $lifetime = null)
{
$this->storage->clear();
@@ -171,7 +171,7 @@ public function invalidate($lifetime = null)
/**
* {@inheritdoc}
*/
- public function migrate($destroy = false, $lifetime = null)
+ public function migrate(bool $destroy = false, int $lifetime = null)
{
return $this->storage->regenerate($destroy, $lifetime);
}
@@ -195,7 +195,7 @@ public function getId()
/**
* {@inheritdoc}
*/
- public function setId($id)
+ public function setId(string $id)
{
if ($this->storage->getId() !== $id) {
$this->storage->setId($id);
@@ -213,7 +213,7 @@ public function getName()
/**
* {@inheritdoc}
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->storage->setName($name);
}
@@ -239,7 +239,7 @@ public function registerBag(SessionBagInterface $bag)
/**
* {@inheritdoc}
*/
- public function getBag($name)
+ public function getBag(string $name)
{
$bag = $this->storage->getBag($name);
diff --git a/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php b/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php
index e758c6bda6018..b2f09fd0dc713 100644
--- a/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php
+++ b/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php
@@ -38,10 +38,8 @@ public function getId();
/**
* Sets the session ID.
- *
- * @param string $id
*/
- public function setId($id);
+ public function setId(string $id);
/**
* Returns the session name.
@@ -52,10 +50,8 @@ public function getName();
/**
* Sets the session name.
- *
- * @param string $name
*/
- public function setName($name);
+ public function setName(string $name);
/**
* Invalidates the current session.
@@ -70,7 +66,7 @@ public function setName($name);
*
* @return bool
*/
- public function invalidate($lifetime = null);
+ public function invalidate(int $lifetime = null);
/**
* Migrates the current session to a new session id while maintaining all
@@ -84,7 +80,7 @@ public function invalidate($lifetime = null);
*
* @return bool
*/
- public function migrate($destroy = false, $lifetime = null);
+ public function migrate(bool $destroy = false, int $lifetime = null);
/**
* Force the session to be saved and closed.
@@ -98,29 +94,25 @@ public function save();
/**
* Checks if an attribute is defined.
*
- * @param string $name The attribute name
- *
* @return bool
*/
- public function has($name);
+ public function has(string $name);
/**
* Returns an attribute.
*
- * @param string $name The attribute name
- * @param mixed $default The default value if not found
+ * @param mixed $default The default value if not found
*
* @return mixed
*/
- public function get($name, $default = null);
+ public function get(string $name, $default = null);
/**
* Sets an attribute.
*
- * @param string $name
- * @param mixed $value
+ * @param mixed $value
*/
- public function set($name, $value);
+ public function set(string $name, $value);
/**
* Returns attributes.
@@ -137,11 +129,9 @@ public function replace(array $attributes);
/**
* Removes an attribute.
*
- * @param string $name
- *
* @return mixed The removed value or null when it does not exist
*/
- public function remove($name);
+ public function remove(string $name);
/**
* Clears all attributes.
@@ -163,11 +153,9 @@ public function registerBag(SessionBagInterface $bag);
/**
* Gets a bag instance by name.
*
- * @param string $name
- *
* @return SessionBagInterface
*/
- public function getBag($name);
+ public function getBag(string $name);
/**
* Gets session meta.
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
index a196833cbf5d3..bd151669c8c03 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
@@ -42,26 +42,19 @@ public function open($savePath, $sessionName)
}
/**
- * @param string $sessionId
- *
* @return string
*/
- abstract protected function doRead($sessionId);
+ abstract protected function doRead(string $sessionId);
/**
- * @param string $sessionId
- * @param string $data
- *
* @return bool
*/
- abstract protected function doWrite($sessionId, $data);
+ abstract protected function doWrite(string $sessionId, string $data);
/**
- * @param string $sessionId
- *
* @return bool
*/
- abstract protected function doDestroy($sessionId);
+ abstract protected function doDestroy(string $sessionId);
/**
* @return bool
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/IdentityMarshaller.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/IdentityMarshaller.php
new file mode 100644
index 0000000000000..bea3a323edb72
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/IdentityMarshaller.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+use Symfony\Component\Cache\Marshaller\MarshallerInterface;
+
+/**
+ * @author Ahmed TAILOULOUTE
+ */
+class IdentityMarshaller implements MarshallerInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function marshall(array $values, ?array &$failed): array
+ {
+ foreach ($values as $key => $value) {
+ if (!\is_string($value)) {
+ throw new \LogicException(sprintf('%s accepts only string as data.', __METHOD__));
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unmarshall(string $value): string
+ {
+ return $value;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MarshallingSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MarshallingSessionHandler.php
new file mode 100644
index 0000000000000..25cd4ec164747
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MarshallingSessionHandler.php
@@ -0,0 +1,100 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+use Symfony\Component\Cache\Marshaller\MarshallerInterface;
+
+/**
+ * @author Ahmed TAILOULOUTE
+ */
+class MarshallingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+ private $handler;
+ private $marshaller;
+
+ public function __construct(AbstractSessionHandler $handler, MarshallerInterface $marshaller)
+ {
+ $this->handler = $handler;
+ $this->marshaller = $marshaller;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $name)
+ {
+ return $this->handler->open($savePath, $name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return $this->handler->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ return $this->handler->destroy($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ return $this->handler->gc($maxlifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ return $this->marshaller->unmarshall($this->handler->read($sessionId));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($sessionId, $data)
+ {
+ $failed = [];
+ $marshalledData = $this->marshaller->marshall(['data' => $data], $failed);
+
+ if (isset($failed['data'])) {
+ return false;
+ }
+
+ return $this->handler->write($sessionId, $marshalledData['data']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateId($sessionId)
+ {
+ return $this->handler->validateId($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return $this->handler->updateTimestamp($sessionId, $data);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php
index a399be5fd8ee7..8896964ee7e55 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php
@@ -65,7 +65,7 @@ public function close()
/**
* {@inheritdoc}
*/
- protected function doRead($sessionId)
+ protected function doRead(string $sessionId)
{
return $this->memcached->get($this->prefix.$sessionId) ?: '';
}
@@ -83,7 +83,7 @@ public function updateTimestamp($sessionId, $data)
/**
* {@inheritdoc}
*/
- protected function doWrite($sessionId, $data)
+ protected function doWrite(string $sessionId, string $data)
{
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
}
@@ -91,7 +91,7 @@ protected function doWrite($sessionId, $data)
/**
* {@inheritdoc}
*/
- protected function doDestroy($sessionId)
+ protected function doDestroy(string $sessionId)
{
$result = $this->memcached->delete($this->prefix.$sessionId);
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php
index 27e08002155bf..a6889e40c7de8 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php
@@ -90,7 +90,7 @@ public function close()
/**
* {@inheritdoc}
*/
- protected function doDestroy($sessionId)
+ protected function doDestroy(string $sessionId)
{
$this->getCollection()->deleteOne([
$this->options['id_field'] => $sessionId,
@@ -114,7 +114,7 @@ public function gc($maxlifetime)
/**
* {@inheritdoc}
*/
- protected function doWrite($sessionId, $data)
+ protected function doWrite(string $sessionId, string $data)
{
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
@@ -154,7 +154,7 @@ public function updateTimestamp($sessionId, $data)
/**
* {@inheritdoc}
*/
- protected function doRead($sessionId)
+ protected function doRead(string $sessionId)
{
$dbData = $this->getCollection()->findOne([
$this->options['id_field'] => $sessionId,
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php
index 0634e46dd53f4..aa0e595c6be65 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php
@@ -37,7 +37,7 @@ public function validateId($sessionId)
/**
* {@inheritdoc}
*/
- protected function doRead($sessionId)
+ protected function doRead(string $sessionId)
{
return '';
}
@@ -53,7 +53,7 @@ public function updateTimestamp($sessionId, $data)
/**
* {@inheritdoc}
*/
- protected function doWrite($sessionId, $data)
+ protected function doWrite(string $sessionId, string $data)
{
return true;
}
@@ -61,7 +61,7 @@ protected function doWrite($sessionId, $data)
/**
* {@inheritdoc}
*/
- protected function doDestroy($sessionId)
+ protected function doDestroy(string $sessionId)
{
return true;
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
index 034c02e5c5911..74412e0083dd4 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -302,7 +302,7 @@ public function gc($maxlifetime)
/**
* {@inheritdoc}
*/
- protected function doDestroy($sessionId)
+ protected function doDestroy(string $sessionId)
{
// delete the record associated with this id
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
@@ -323,7 +323,7 @@ protected function doDestroy($sessionId)
/**
* {@inheritdoc}
*/
- protected function doWrite($sessionId, $data)
+ protected function doWrite(string $sessionId, string $data)
{
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
@@ -606,11 +606,9 @@ private function rollback(): void
* We need to make sure we do not return session data that is already considered garbage according
* to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes.
*
- * @param string $sessionId Session ID
- *
- * @return string The session data
+ * @return string
*/
- protected function doRead($sessionId)
+ protected function doRead(string $sessionId)
{
if (self::LOCK_ADVISORY === $this->lockMode) {
$this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php
index e5be90d5f3b53..b9ca28f5909f3 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php
@@ -69,7 +69,7 @@ public function __construct($redis, array $options = [])
/**
* {@inheritdoc}
*/
- protected function doRead($sessionId): string
+ protected function doRead(string $sessionId): string
{
return $this->redis->get($this->prefix.$sessionId) ?: '';
}
@@ -77,7 +77,7 @@ protected function doRead($sessionId): string
/**
* {@inheritdoc}
*/
- protected function doWrite($sessionId, $data): bool
+ protected function doWrite(string $sessionId, string $data): bool
{
$result = $this->redis->setEx($this->prefix.$sessionId, (int) ($this->ttl ?? ini_get('session.gc_maxlifetime')), $data);
@@ -87,7 +87,7 @@ protected function doWrite($sessionId, $data): bool
/**
* {@inheritdoc}
*/
- protected function doDestroy($sessionId): bool
+ protected function doDestroy(string $sessionId): bool
{
$this->redis->del($this->prefix.$sessionId);
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php
index 3144ea597ea6b..4292a3b2f0828 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/StrictSessionHandler.php
@@ -43,7 +43,7 @@ public function open($savePath, $sessionName)
/**
* {@inheritdoc}
*/
- protected function doRead($sessionId)
+ protected function doRead(string $sessionId)
{
return $this->handler->read($sessionId);
}
@@ -59,7 +59,7 @@ public function updateTimestamp($sessionId, $data)
/**
* {@inheritdoc}
*/
- protected function doWrite($sessionId, $data)
+ protected function doWrite(string $sessionId, string $data)
{
return $this->handler->write($sessionId, $data);
}
@@ -78,7 +78,7 @@ public function destroy($sessionId)
/**
* {@inheritdoc}
*/
- protected function doDestroy($sessionId)
+ protected function doDestroy(string $sessionId)
{
$this->doDestroy = false;
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php
index 5fe40fc106183..c79ee023c4e50 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php
@@ -100,7 +100,7 @@ public function getLifetime()
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*/
- public function stampNew($lifetime = null)
+ public function stampNew(int $lifetime = null)
{
$this->stampCreated($lifetime);
}
@@ -151,10 +151,8 @@ public function getName()
/**
* Sets name.
- *
- * @param string $name
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
index 37b6f145b87cc..4662bd864c944 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
@@ -94,7 +94,7 @@ public function start()
/**
* {@inheritdoc}
*/
- public function regenerate($destroy = false, $lifetime = null)
+ public function regenerate(bool $destroy = false, int $lifetime = null)
{
if (!$this->started) {
$this->start();
@@ -117,7 +117,7 @@ public function getId()
/**
* {@inheritdoc}
*/
- public function setId($id)
+ public function setId(string $id)
{
if ($this->started) {
throw new \LogicException('Cannot set session ID after the session has started.');
@@ -137,7 +137,7 @@ public function getName()
/**
* {@inheritdoc}
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->name = $name;
}
@@ -183,7 +183,7 @@ public function registerBag(SessionBagInterface $bag)
/**
* {@inheritdoc}
*/
- public function getBag($name)
+ public function getBag(string $name)
{
if (!isset($this->bags[$name])) {
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
index 02fe4dad48dd6..019a63484e2e7 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
@@ -28,7 +28,6 @@ class MockFileSessionStorage extends MockArraySessionStorage
/**
* @param string $savePath Path of directory to save session files
- * @param string $name Session name
*/
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
{
@@ -68,7 +67,7 @@ public function start()
/**
* {@inheritdoc}
*/
- public function regenerate($destroy = false, $lifetime = null)
+ public function regenerate(bool $destroy = false, int $lifetime = null)
{
if (!$this->started) {
$this->start();
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
index 4085bd6ef549c..792bacf4e4ca5 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
@@ -175,7 +175,7 @@ public function getId()
/**
* {@inheritdoc}
*/
- public function setId($id)
+ public function setId(string $id)
{
$this->saveHandler->setId($id);
}
@@ -191,7 +191,7 @@ public function getName()
/**
* {@inheritdoc}
*/
- public function setName($name)
+ public function setName(string $name)
{
$this->saveHandler->setName($name);
}
@@ -199,7 +199,7 @@ public function setName($name)
/**
* {@inheritdoc}
*/
- public function regenerate($destroy = false, $lifetime = null)
+ public function regenerate(bool $destroy = false, int $lifetime = null)
{
// Cannot regenerate the session ID for non-active sessions.
if (PHP_SESSION_ACTIVE !== session_status()) {
@@ -308,7 +308,7 @@ public function registerBag(SessionBagInterface $bag)
/**
* {@inheritdoc}
*/
- public function getBag($name)
+ public function getBag(string $name)
{
if (!isset($this->bags[$name])) {
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php
index cd0635a1684d7..38f7dede60e52 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php
@@ -81,11 +81,9 @@ public function getId()
/**
* Sets the session ID.
*
- * @param string $id
- *
* @throws \LogicException
*/
- public function setId($id)
+ public function setId(string $id)
{
if ($this->isActive()) {
throw new \LogicException('Cannot change the ID of an active session');
@@ -107,11 +105,9 @@ public function getName()
/**
* Sets the session name.
*
- * @param string $name
- *
* @throws \LogicException
*/
- public function setName($name)
+ public function setName(string $name)
{
if ($this->isActive()) {
throw new \LogicException('Cannot change the name of an active session');
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
index eeb396a2f131c..eb8e8ff2357e5 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
@@ -46,24 +46,20 @@ public function getId();
/**
* Sets the session ID.
- *
- * @param string $id
*/
- public function setId($id);
+ public function setId(string $id);
/**
* Returns the session name.
*
- * @return mixed The session name
+ * @return string The session name
*/
public function getName();
/**
* Sets the session name.
- *
- * @param string $name
*/
- public function setName($name);
+ public function setName(string $name);
/**
* Regenerates id that represents this storage.
@@ -94,7 +90,7 @@ public function setName($name);
*
* @throws \RuntimeException If an error occurs while regenerating this storage
*/
- public function regenerate($destroy = false, $lifetime = null);
+ public function regenerate(bool $destroy = false, int $lifetime = null);
/**
* Force the session to be saved and closed.
@@ -117,13 +113,11 @@ public function clear();
/**
* Gets a SessionBagInterface by name.
*
- * @param string $name
- *
* @return SessionBagInterface
*
* @throws \InvalidArgumentException If the bag does not exist
*/
- public function getBag($name);
+ public function getBag(string $name);
/**
* Registers a SessionBagInterface for use.
diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php
index ef8095bbe22e6..7cbf453188816 100644
--- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php
+++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php
@@ -30,11 +30,6 @@ class StreamedResponse extends Response
protected $streamed;
private $headersSent;
- /**
- * @param callable|null $callback A valid PHP callback or null to set it later
- * @param int $status The response status code
- * @param array $headers An array of response headers
- */
public function __construct(callable $callback = null, int $status = 200, array $headers = [])
{
parent::__construct(null, $status, $headers);
@@ -50,13 +45,15 @@ public function __construct(callable $callback = null, int $status = 200, array
* Factory method for chainability.
*
* @param callable|null $callback A valid PHP callback or null to set it later
- * @param int $status The response status code
- * @param array $headers An array of response headers
*
* @return static
+ *
+ * @deprecated since Symfony 5.1, use __construct() instead.
*/
- public static function create($callback = null, $status = 200, $headers = [])
+ public static function create($callback = null, int $status = 200, array $headers = [])
{
+ trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, \get_called_class());
+
return new static($callback, $status, $headers);
}
@@ -121,7 +118,7 @@ public function sendContent()
*
* @return $this
*/
- public function setContent($content)
+ public function setContent(?string $content)
{
if (null !== $content) {
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ApacheRequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/ApacheRequestTest.php
deleted file mode 100644
index 7a5bd378a200c..0000000000000
--- a/src/Symfony/Component/HttpFoundation/Tests/ApacheRequestTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\HttpFoundation\ApacheRequest;
-
-/** @group legacy */
-class ApacheRequestTest extends TestCase
-{
- /**
- * @dataProvider provideServerVars
- */
- public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo)
- {
- $request = new ApacheRequest();
- $request->server->replace($server);
-
- $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
- $this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct');
- $this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct');
- }
-
- public function provideServerVars()
- {
- return [
- [
- [
- 'REQUEST_URI' => '/foo/app_dev.php/bar',
- 'SCRIPT_NAME' => '/foo/app_dev.php',
- 'PATH_INFO' => '/bar',
- ],
- '/foo/app_dev.php/bar',
- '/foo/app_dev.php',
- '/bar',
- ],
- [
- [
- 'REQUEST_URI' => '/foo/bar',
- 'SCRIPT_NAME' => '/foo/app_dev.php',
- ],
- '/foo/bar',
- '/foo',
- '/bar',
- ],
- [
- [
- 'REQUEST_URI' => '/app_dev.php/foo/bar',
- 'SCRIPT_NAME' => '/app_dev.php',
- 'PATH_INFO' => '/foo/bar',
- ],
- '/app_dev.php/foo/bar',
- '/app_dev.php',
- '/foo/bar',
- ],
- [
- [
- 'REQUEST_URI' => '/foo/bar',
- 'SCRIPT_NAME' => '/app_dev.php',
- ],
- '/foo/bar',
- '',
- '/foo/bar',
- ],
- [
- [
- 'REQUEST_URI' => '/app_dev.php',
- 'SCRIPT_NAME' => '/app_dev.php',
- ],
- '/app_dev.php',
- '/app_dev.php',
- '/',
- ],
- [
- [
- 'REQUEST_URI' => '/',
- 'SCRIPT_NAME' => '/app_dev.php',
- ],
- '/',
- '',
- '/',
- ],
- ];
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
index 55287e082d996..ef9c13e4c4508 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
@@ -47,6 +47,15 @@ public function testInstantiationThrowsExceptionIfRawCookieNameContainsSpecialCh
Cookie::create($name, null, 0, null, null, null, false, true);
}
+ /**
+ * @dataProvider namesWithSpecialCharacters
+ */
+ public function testWithRawThrowsExceptionIfCookieNameContainsSpecialCharacters($name)
+ {
+ $this->expectException('InvalidArgumentException');
+ Cookie::create($name)->withRaw();
+ }
+
/**
* @dataProvider namesWithSpecialCharacters
*/
@@ -72,6 +81,10 @@ public function testNegativeExpirationIsNotPossible()
$cookie = Cookie::create('foo', 'bar', -100);
$this->assertSame(0, $cookie->getExpiresTime());
+
+ $cookie = Cookie::create('foo', 'bar')->withExpires(-100);
+
+ $this->assertSame(0, $cookie->getExpiresTime());
}
public function testGetValue()
@@ -98,6 +111,10 @@ public function testGetExpiresTime()
$cookie = Cookie::create('foo', 'bar', $expire = time() + 3600);
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+
+ $cookie = Cookie::create('foo')->withExpires($expire = time() + 3600);
+
+ $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testGetExpiresTimeIsCastToInt()
@@ -105,6 +122,10 @@ public function testGetExpiresTimeIsCastToInt()
$cookie = Cookie::create('foo', 'bar', 3600.9);
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
+
+ $cookie = Cookie::create('foo')->withExpires(3600.6);
+
+ $this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
}
public function testConstructorWithDateTime()
@@ -113,6 +134,10 @@ public function testConstructorWithDateTime()
$cookie = Cookie::create('foo', 'bar', $expire);
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+
+ $cookie = Cookie::create('foo')->withExpires($expire);
+
+ $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testConstructorWithDateTimeImmutable()
@@ -121,6 +146,10 @@ public function testConstructorWithDateTimeImmutable()
$cookie = Cookie::create('foo', 'bar', $expire);
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+
+ $cookie = Cookie::create('foo')->withValue('bar')->withExpires($expire);
+
+ $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testGetExpiresTimeWithStringValue()
@@ -130,6 +159,10 @@ public function testGetExpiresTimeWithStringValue()
$expire = strtotime($value);
$this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');
+
+ $cookie = Cookie::create('foo')->withValue('bar')->withExpires($value);
+
+ $this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');
}
public function testGetDomain()
@@ -137,6 +170,10 @@ public function testGetDomain()
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com');
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
+
+ $cookie = Cookie::create('foo')->withDomain('.mybardomain.com');
+
+ $this->assertEquals('.mybardomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
}
public function testIsSecure()
@@ -144,6 +181,10 @@ public function testIsSecure()
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', true);
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
+
+ $cookie = Cookie::create('foo')->withSecure(true);
+
+ $this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
}
public function testIsHttpOnly()
@@ -151,6 +192,10 @@ public function testIsHttpOnly()
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
+
+ $cookie = Cookie::create('foo')->withHttpOnly(true);
+
+ $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
}
public function testCookieIsNotCleared()
@@ -158,6 +203,10 @@ public function testCookieIsNotCleared()
$cookie = Cookie::create('foo', 'bar', time() + 3600 * 24);
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
+
+ $cookie = Cookie::create('foo')->withExpires(time() + 3600 * 24);
+
+ $this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
}
public function testCookieIsCleared()
@@ -166,6 +215,10 @@ public function testCookieIsCleared()
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
+ $cookie = Cookie::create('foo')->withExpires(time() - 20);
+
+ $this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
+
$cookie = Cookie::create('foo', 'bar');
$this->assertFalse($cookie->isCleared());
@@ -177,21 +230,55 @@ public function testCookieIsCleared()
$cookie = Cookie::create('foo', 'bar', -1);
$this->assertFalse($cookie->isCleared());
+
+ $cookie = Cookie::create('foo')->withExpires(-1);
+
+ $this->assertFalse($cookie->isCleared());
}
public function testToString()
{
+ $expected = 'foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly';
$cookie = Cookie::create('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
- $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
+ $this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of the cookie');
+
+ $cookie = Cookie::create('foo')
+ ->withValue('bar')
+ ->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
+ ->withDomain('.myfoodomain.com')
+ ->withSecure(true)
+ ->withSameSite(null);
+ $this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of the cookie');
+ $expected = 'foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly';
$cookie = Cookie::create('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
- $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
+ $this->assertEquals($expected, (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
+ $cookie = Cookie::create('foo')
+ ->withValue('bar with white spaces')
+ ->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
+ ->withDomain('.myfoodomain.com')
+ ->withSecure(true)
+ ->withSameSite(null);
+ $this->assertEquals($expected, (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
+
+ $expected = 'foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly';
$cookie = Cookie::create('foo', null, 1, '/admin/', '.myfoodomain.com', false, true, false, null);
- $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
+ $this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
+
+ $cookie = Cookie::create('foo')
+ ->withExpires(1)
+ ->withPath('/admin/')
+ ->withDomain('.myfoodomain.com')
+ ->withSameSite(null);
+ $this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
+ $expected = 'foo=bar; path=/; httponly; samesite=lax';
$cookie = Cookie::create('foo', 'bar');
- $this->assertEquals('foo=bar; path=/; httponly; samesite=lax', (string) $cookie);
+ $this->assertEquals($expected, (string) $cookie);
+
+ $cookie = Cookie::create('foo')->withValue('bar');
+ $this->assertEquals($expected, (string) $cookie);
}
public function testRawCookie()
@@ -200,9 +287,21 @@ public function testRawCookie()
$this->assertFalse($cookie->isRaw());
$this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);
+ $cookie = Cookie::create('test')->withValue('t e s t')->withHttpOnly(false)->withSameSite(null);
+ $this->assertFalse($cookie->isRaw());
+ $this->assertEquals('test=t%20e%20s%20t; path=/', (string) $cookie);
+
$cookie = Cookie::create('foo', 'b+a+r', 0, '/', null, false, false, true, null);
$this->assertTrue($cookie->isRaw());
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
+
+ $cookie = Cookie::create('foo')
+ ->withValue('t+e+s+t')
+ ->withHttpOnly(false)
+ ->withRaw(true)
+ ->withSameSite(null);
+ $this->assertTrue($cookie->isRaw());
+ $this->assertEquals('foo=t+e+s+t; path=/', (string) $cookie);
}
public function testGetMaxAge()
@@ -245,6 +344,9 @@ public function testSameSiteAttribute()
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, '');
$this->assertNull($cookie->getSameSite());
+
+ $cookie = Cookie::create('foo')->withSameSite('Lax');
+ $this->assertEquals('lax', $cookie->getSameSite());
}
public function testSetSecureDefault()
diff --git a/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.php b/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.php
deleted file mode 100644
index c566db7693d49..0000000000000
--- a/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.php
+++ /dev/null
@@ -1,100 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\Tests\File\MimeType;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
-use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
-
-/**
- * @requires extension fileinfo
- * @group legacy
- */
-class MimeTypeTest extends TestCase
-{
- public function testGuessWithLeadingDash()
- {
- $cwd = getcwd();
- chdir(__DIR__.'/../Fixtures');
- try {
- $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess('-test'));
- } finally {
- chdir($cwd);
- }
- }
-
- public function testGuessImageWithoutExtension()
- {
- $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
- }
-
- public function testGuessImageWithDirectory()
- {
- $this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
-
- MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
- }
-
- public function testGuessImageWithFileBinaryMimeTypeGuesser()
- {
- $guesser = MimeTypeGuesser::getInstance();
- $guesser->register(new FileBinaryMimeTypeGuesser());
- $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
- }
-
- public function testGuessImageWithKnownExtension()
- {
- $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
- }
-
- public function testGuessFileWithUnknownExtension()
- {
- $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
- }
-
- public function testGuessWithIncorrectPath()
- {
- $this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
- MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
- }
-
- public function testGuessWithNonReadablePath()
- {
- if ('\\' === \DIRECTORY_SEPARATOR) {
- $this->markTestSkipped('Can not verify chmod operations on Windows');
- }
-
- if (!getenv('USER') || 'root' === getenv('USER')) {
- $this->markTestSkipped('This test will fail if run under superuser');
- }
-
- $path = __DIR__.'/../Fixtures/to_delete';
- touch($path);
- @chmod($path, 0333);
-
- if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
- $this->expectException('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
- MimeTypeGuesser::getInstance()->guess($path);
- } else {
- $this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
- }
- }
-
- public static function tearDownAfterClass(): void
- {
- $path = __DIR__.'/../Fixtures/to_delete';
- if (file_exists($path)) {
- @chmod($path, 0666);
- @unlink($path);
- }
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php b/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php
index 17d319581f97f..de30adc16ee72 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php
@@ -259,40 +259,6 @@ public function testGetSize()
$this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize());
}
- /**
- * @group legacy
- * @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1.
- */
- public function testConstructDeprecatedSize()
- {
- $file = new UploadedFile(
- __DIR__.'/Fixtures/test.gif',
- 'original.gif',
- 'image/gif',
- filesize(__DIR__.'/Fixtures/test.gif'),
- UPLOAD_ERR_OK,
- false
- );
-
- $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
- }
-
- /**
- * @group legacy
- * @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1.
- */
- public function testConstructDeprecatedSizeWhenPassingOnlyThe4Needed()
- {
- $file = new UploadedFile(
- __DIR__.'/Fixtures/test.gif',
- 'original.gif',
- 'image/gif',
- filesize(__DIR__.'/Fixtures/test.gif')
- );
-
- $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
- }
-
public function testGetExtension()
{
$file = new UploadedFile(
diff --git a/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php
index 3ce4a7dd4dfc3..89fdf82ac2aa8 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php
@@ -108,19 +108,6 @@ public function testGet()
$this->assertNull($bag->get('baz', 'nope'), '->get return null although different default value is given');
}
- /**
- * @group legacy
- * @expectedDeprecation Passing a third argument to "Symfony\Component\HttpFoundation\HeaderBag::get()" is deprecated since Symfony 4.4, use method "all()" instead
- */
- public function testGetIsEqualToNewMethod()
- {
- $bag = new HeaderBag(['foo' => 'bar', 'fuzz' => 'bizz']);
- $this->assertSame($bag->all('none'), $bag->get('none', [], false), '->get unknown values returns default as array');
-
- $bag->set('foo', 'bor', false);
- $this->assertSame(['bar', 'bor'], $bag->get('foo', 'nope', false), '->get return all values as array');
- }
-
public function testSetAssociativeArray()
{
$bag = new HeaderBag();
diff --git a/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php
index aa8441799b2b6..ca9cfb1ceec0b 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php
@@ -90,6 +90,9 @@ public function testSetJson()
$this->assertEquals('true', $response->getContent());
}
+ /**
+ * @group legacy
+ */
public function testCreate()
{
$response = JsonResponse::create(['foo' => 'bar'], 204);
@@ -99,6 +102,9 @@ public function testCreate()
$this->assertEquals(204, $response->getStatusCode());
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateEmptyJsonObject()
{
$response = JsonResponse::create();
@@ -106,6 +112,9 @@ public function testStaticCreateEmptyJsonObject()
$this->assertSame('{}', $response->getContent());
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateJsonArray()
{
$response = JsonResponse::create([0, 1, 2, 3]);
@@ -113,6 +122,9 @@ public function testStaticCreateJsonArray()
$this->assertSame('[0,1,2,3]', $response->getContent());
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateJsonObject()
{
$response = JsonResponse::create(['foo' => 'bar']);
@@ -120,6 +132,9 @@ public function testStaticCreateJsonObject()
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateWithSimpleTypes()
{
$response = JsonResponse::create('foo');
@@ -140,18 +155,27 @@ public function testStaticCreateWithSimpleTypes()
$this->assertSame('true', $response->getContent());
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateWithCustomStatus()
{
$response = JsonResponse::create([], 202);
$this->assertSame(202, $response->getStatusCode());
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateAddsContentTypeHeader()
{
$response = JsonResponse::create();
$this->assertSame('application/json', $response->headers->get('Content-Type'));
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateWithCustomHeaders()
{
$response = JsonResponse::create([], 200, ['ETag' => 'foo']);
@@ -159,6 +183,9 @@ public function testStaticCreateWithCustomHeaders()
$this->assertSame('foo', $response->headers->get('ETag'));
}
+ /**
+ * @group legacy
+ */
public function testStaticCreateWithCustomContentType()
{
$headers = ['Content-Type' => 'application/vnd.acme.blog-v1+json'];
@@ -169,7 +196,7 @@ public function testStaticCreateWithCustomContentType()
public function testSetCallback()
{
- $response = JsonResponse::create(['foo' => 'bar'])->setCallback('callback');
+ $response = (new JsonResponse(['foo' => 'bar']))->setCallback('callback');
$this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent());
$this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
@@ -217,7 +244,7 @@ public function testSetCallbackInvalidIdentifier()
public function testSetContent()
{
$this->expectException('InvalidArgumentException');
- JsonResponse::create("\xB1\x31");
+ new JsonResponse("\xB1\x31");
}
public function testSetContentJsonSerializeError()
@@ -230,12 +257,12 @@ public function testSetContentJsonSerializeError()
$serializable = new JsonSerializableObject();
- JsonResponse::create($serializable);
+ new JsonResponse($serializable);
}
public function testSetComplexCallback()
{
- $response = JsonResponse::create(['foo' => 'bar']);
+ $response = new JsonResponse(['foo' => 'bar']);
$response->setCallback('ಠ_ಠ["foo"].bar[0]');
$this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent());
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php
index 9d9797f7cfd3f..1d01725d16a08 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php
@@ -59,13 +59,9 @@ public function testSetTargetUrl()
$this->assertEquals('baz.beep', $response->getTargetUrl());
}
- public function testSetTargetUrlNull()
- {
- $this->expectException('InvalidArgumentException');
- $response = new RedirectResponse('foo.bar');
- $response->setTargetUrl(null);
- }
-
+ /**
+ * @group legacy
+ */
public function testCreate()
{
$response = RedirectResponse::create('foo', 301);
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 7c53ec2d53655..b8c57fc92418e 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -424,7 +424,7 @@ public function testGetPreferredFormat()
}
/**
- * @dataProvider getFormatToMimeTypeMapProviderWithAdditionalNullFormat
+ * @dataProvider getFormatToMimeTypeMapProvider
*/
public function testGetFormatFromMimeType($format, $mimeTypes)
{
@@ -442,14 +442,6 @@ public function testGetFormatFromMimeType($format, $mimeTypes)
}
}
- public function getFormatToMimeTypeMapProviderWithAdditionalNullFormat()
- {
- return array_merge(
- [[null, [null, 'unexistent-mime-type']]],
- $this->getFormatToMimeTypeMapProvider()
- );
- }
-
public function testGetFormatFromMimeTypeWithParameters()
{
$request = new Request();
@@ -1624,15 +1616,6 @@ public function testGetSession()
$this->assertObjectHasAttribute('attributeName', $session);
}
- /**
- * @group legacy
- * @expectedDeprecation Calling "Symfony\Component\HttpFoundation\Request::getSession()" when no session has been set is deprecated since Symfony 4.1 and will throw an exception in 5.0. Use "hasSession()" instead.
- */
- public function testGetSessionNullable()
- {
- (new Request())->getSession();
- }
-
public function testHasPreviousSession()
{
$request = new Request();
@@ -2342,6 +2325,64 @@ public function trustedProxiesRemoteAddr()
[null, ['REMOTE_ADDR', '2.2.2.2'], ['2.2.2.2']],
];
}
+
+ /**
+ * @dataProvider preferSafeContentData
+ */
+ public function testPreferSafeContent($server, bool $safePreferenceExpected)
+ {
+ $request = new Request([], [], [], [], [], $server);
+
+ $this->assertEquals($safePreferenceExpected, $request->preferSafeContent());
+ }
+
+ public function preferSafeContentData()
+ {
+ return [
+ [[], false],
+ [
+ [
+ 'HTTPS' => 'on',
+ ],
+ false,
+ ],
+ [
+ [
+ 'HTTPS' => 'off',
+ 'HTTP_PREFER' => 'safe',
+ ],
+ false,
+ ],
+ [
+ [
+ 'HTTPS' => 'on',
+ 'HTTP_PREFER' => 'safe',
+ ],
+ true,
+ ],
+ [
+ [
+ 'HTTPS' => 'on',
+ 'HTTP_PREFER' => 'unknown-preference',
+ ],
+ false,
+ ],
+ [
+ [
+ 'HTTPS' => 'on',
+ 'HTTP_PREFER' => 'unknown-preference=42, safe',
+ ],
+ true,
+ ],
+ [
+ [
+ 'HTTPS' => 'on',
+ 'HTTP_PREFER' => 'safe, unknown-preference=42',
+ ],
+ true,
+ ],
+ ];
+ }
}
class RequestContentProxy extends Request
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
index d5da59ad7e803..dd04d3f5b431a 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
@@ -20,6 +20,9 @@
*/
class ResponseTest extends ResponseTestCase
{
+ /**
+ * @group legacy
+ */
public function testCreate()
{
$response = Response::create('foo', 301, ['Foo' => 'bar']);
@@ -898,16 +901,6 @@ public function testSetContent($content)
$this->assertEquals((string) $content, $response->getContent());
}
- /**
- * @dataProvider invalidContentProvider
- */
- public function testSetContentInvalid($content)
- {
- $this->expectException('UnexpectedValueException');
- $response = new Response();
- $response->setContent($content);
- }
-
public function testSettersAreChainable()
{
$response = new Response();
@@ -949,15 +942,6 @@ public function validContentProvider()
];
}
- public function invalidContentProvider()
- {
- return [
- 'obj' => [new \stdClass()],
- 'array' => [[]],
- 'bool' => [true, '1'],
- ];
- }
-
protected function createDateTimeOneHourAgo()
{
return $this->createDateTimeNow()->sub(new \DateInterval('PT1H'));
@@ -1048,6 +1032,24 @@ public function testReasonPhraseDefaultsAgainstIana($code, $reasonPhrase)
{
$this->assertEquals($reasonPhrase, Response::$statusTexts[$code]);
}
+
+ public function testSetContentSafe()
+ {
+ $response = new Response();
+
+ $this->assertFalse($response->headers->has('Preference-Applied'));
+ $this->assertFalse($response->headers->has('Vary'));
+
+ $response->setContentSafe();
+
+ $this->assertSame('safe', $response->headers->get('Preference-Applied'));
+ $this->assertSame('Prefer', $response->headers->get('Vary'));
+
+ $response->setContentSafe(false);
+
+ $this->assertFalse($response->headers->has('Preference-Applied'));
+ $this->assertSame('Prefer', $response->headers->get('Vary'));
+ }
}
class StringableObject
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php
index 8828be666f2dc..de794075e44d3 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php
@@ -35,7 +35,7 @@ abstract class AbstractRedisSessionHandlerTestCase extends TestCase
/**
* @return \Redis|\RedisArray|\RedisCluster|\Predis\Client
*/
- abstract protected function createRedisClient(string $host);
+ abstract protected function createRedisClient(string $host): object;
protected function setUp(): void
{
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/IdentityMarshallerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/IdentityMarshallerTest.php
new file mode 100644
index 0000000000000..b26bc7e60a6bb
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/IdentityMarshallerTest.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller;
+
+/**
+ * @author Ahmed TAILOULOUTE
+ */
+class IdentityMarshallerTest extends Testcase
+{
+ public function testMarshall()
+ {
+ $marshaller = new IdentityMarshaller();
+ $values = ['data' => 'string_data'];
+ $failed = [];
+
+ $this->assertSame($values, $marshaller->marshall($values, $failed));
+ }
+
+ /**
+ * @dataProvider invalidMarshallDataProvider
+ */
+ public function testMarshallInvalidData($values)
+ {
+ $marshaller = new IdentityMarshaller();
+ $failed = [];
+
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessage('Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller::marshall accepts only string as data');
+
+ $marshaller->marshall($values, $failed);
+ }
+
+ public function testUnmarshall()
+ {
+ $marshaller = new IdentityMarshaller();
+
+ $this->assertEquals('data', $marshaller->unmarshall('data'));
+ }
+
+ public function invalidMarshallDataProvider(): iterable
+ {
+ return [
+ [['object' => new \stdClass()]],
+ [['foo' => ['bar']]],
+ ];
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MarshallingSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MarshallingSessionHandlerTest.php
new file mode 100644
index 0000000000000..e9eb46801e7c8
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MarshallingSessionHandlerTest.php
@@ -0,0 +1,128 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Cache\Marshaller\MarshallerInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MarshallingSessionHandler;
+
+/**
+ * @author Ahmed TAILOULOUTE
+ */
+class MarshallingSessionHandlerTest extends TestCase
+{
+ /**
+ * @var MockObject|\SessionHandlerInterface
+ */
+ protected $handler;
+
+ /**
+ * @var MockObject|MarshallerInterface
+ */
+ protected $marshaller;
+
+ protected function setUp(): void
+ {
+ $this->marshaller = $this->getMockBuilder(MarshallerInterface::class)->getMock();
+ $this->handler = $this->getMockBuilder(AbstractSessionHandler::class)->getMock();
+ }
+
+ public function testOpen()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('open')
+ ->with('path', 'name')->willReturn(true);
+
+ $marshallingSessionHandler->open('path', 'name');
+ }
+
+ public function testClose()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('close')->willReturn(true);
+
+ $this->assertTrue($marshallingSessionHandler->close());
+ }
+
+ public function testDestroy()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('destroy')
+ ->with('session_id')->willReturn(true);
+
+ $marshallingSessionHandler->destroy('session_id');
+ }
+
+ public function testGc()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('gc')
+ ->with('maxlifetime')->willReturn(true);
+
+ $marshallingSessionHandler->gc('maxlifetime');
+ }
+
+ public function testRead()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('read')->with('session_id')
+ ->willReturn('data');
+ $this->marshaller->expects($this->once())->method('unmarshall')->with('data')
+ ->willReturn('unmarshalled_data')
+ ;
+
+ $result = $marshallingSessionHandler->read('session_id');
+ $this->assertEquals('unmarshalled_data', $result);
+ }
+
+ public function testWrite()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->marshaller->expects($this->once())->method('marshall')
+ ->with(['data' => 'data'], [])
+ ->willReturn(['data' => 'marshalled_data']);
+
+ $this->handler->expects($this->once())->method('write')
+ ->with('session_id', 'marshalled_data')
+ ;
+
+ $marshallingSessionHandler->write('session_id', 'data');
+ }
+
+ public function testValidateId()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('validateId')
+ ->with('session_id')->willReturn(true);
+
+ $marshallingSessionHandler->validateId('session_id');
+ }
+
+ public function testUpdateTimestamp()
+ {
+ $marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
+
+ $this->handler->expects($this->once())->method('updateTimestamp')
+ ->with('session_id', 'data')->willReturn(true);
+
+ $marshallingSessionHandler->updateTimestamp('session_id', 'data');
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php
index 622b42da1a28e..4d59bfc30c64d 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php
@@ -15,7 +15,10 @@
class PredisClusterSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
{
- protected function createRedisClient(string $host): Client
+ /**
+ * @return Client
+ */
+ protected function createRedisClient(string $host): object
{
return new Client([['host' => $host]]);
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php
index 5ecab116f731c..1c9f0ec784a84 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php
@@ -15,7 +15,10 @@
class PredisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
{
- protected function createRedisClient(string $host): Client
+ /**
+ * @return Client
+ */
+ protected function createRedisClient(string $host): object
{
return new Client(['host' => $host]);
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php
index 3ef6cb694b98f..59dd3ddb3c58a 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php
@@ -14,9 +14,9 @@
class RedisArraySessionHandlerTest extends AbstractRedisSessionHandlerTestCase
{
/**
- * @return \RedisArray|object
+ * @return \RedisArray
*/
- protected function createRedisClient(string $host)
+ protected function createRedisClient(string $host): object
{
return new \RedisArray([$host]);
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php
index 8b4cd1cdd61b3..ef9598dadc420 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php
@@ -25,9 +25,9 @@ public static function setUpBeforeClass(): void
}
/**
- * @return \RedisCluster|object
+ * @return \RedisCluster
*/
- protected function createRedisClient(string $host)
+ protected function createRedisClient(string $host): object
{
return new \RedisCluster(null, explode(' ', getenv('REDIS_CLUSTER_HOSTS')));
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php
index 71658f072354c..e1026ffb10522 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php
@@ -14,9 +14,9 @@
class RedisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
{
/**
- * @return \Redis|object
+ * @return \Redis
*/
- protected function createRedisClient(string $host)
+ protected function createRedisClient(string $host): object
{
$client = new \Redis();
$client->connect($host);
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
index ace6249394365..7c5c53b48c5aa 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -196,13 +196,6 @@ public function testSessionOptions()
$this->assertSame('200', ini_get('session.cache_expire'));
}
- public function testSetSaveHandlerException()
- {
- $this->expectException('InvalidArgumentException');
- $storage = $this->getStorage();
- $storage->setSaveHandler(new \stdClass());
- }
-
public function testSetSaveHandler()
{
$this->iniSet('session.save_handler', 'files');
diff --git a/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php
index a084e917dcc0e..6f04271856752 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php
@@ -101,6 +101,9 @@ public function testGetContent()
$this->assertFalse($response->getContent());
}
+ /**
+ * @group legacy
+ */
public function testCreate()
{
$response = StreamedResponse::create(function () {}, 204);
diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json
index efc4b94255588..b214a11562b5b 100644
--- a/src/Symfony/Component/HttpFoundation/composer.json
+++ b/src/Symfony/Component/HttpFoundation/composer.json
@@ -16,13 +16,18 @@
}
],
"require": {
- "php": "^7.1.3",
- "symfony/mime": "^4.3|^5.0",
+ "php": "^7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
"symfony/polyfill-mbstring": "~1.1"
},
"require-dev": {
"predis/predis": "~1.0",
- "symfony/expression-language": "^3.4|^4.0|^5.0"
+ "symfony/cache": "^4.4|^5.0",
+ "symfony/mime": "^4.4|^5.0",
+ "symfony/expression-language": "^4.4|^5.0"
+ },
+ "suggest" : {
+ "symfony/mime": "To use the file extension guesser"
},
"autoload": {
"psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },
@@ -33,7 +38,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.4-dev"
+ "dev-master": "5.1-dev"
}
}
}
diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md
index 08a8cfddd7332..ada9fafe60102 100644
--- a/src/Symfony/Component/HttpKernel/CHANGELOG.md
+++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md
@@ -1,6 +1,40 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * allowed using public aliases to reference controllers
+ * added session usage reporting when the `_stateless` attribute of the request is set to `true`
+
+5.0.0
+-----
+
+ * removed support for getting the container from a non-booted kernel
+ * removed the first and second constructor argument of `ConfigDataCollector`
+ * removed `ConfigDataCollector::getApplicationName()`
+ * removed `ConfigDataCollector::getApplicationVersion()`
+ * removed support for `Symfony\Component\Templating\EngineInterface` in `HIncludeFragmentRenderer`, use a `Twig\Environment` only
+ * removed `TranslatorListener` in favor of `LocaleAwareListener`
+ * removed `getRootDir()` and `getName()` from `Kernel` and `KernelInterface`
+ * removed `FilterControllerArgumentsEvent`, use `ControllerArgumentsEvent` instead
+ * removed `FilterControllerEvent`, use `ControllerEvent` instead
+ * removed `FilterResponseEvent`, use `ResponseEvent` instead
+ * removed `GetResponseEvent`, use `RequestEvent` instead
+ * removed `GetResponseForControllerResultEvent`, use `ViewEvent` instead
+ * removed `GetResponseForExceptionEvent`, use `ExceptionEvent` instead
+ * removed `PostResponseEvent`, use `TerminateEvent` instead
+ * removed `SaveSessionListener` in favor of `AbstractSessionListener`
+ * removed `Client`, use `HttpKernelBrowser` instead
+ * added method `getProjectDir()` to `KernelInterface`
+ * removed methods `serialize` and `unserialize` from `DataCollector`, store the serialized state in the data property instead
+ * made `ProfilerStorageInterface` internal
+ * removed the second and third argument of `KernelInterface::locateResource`
+ * removed the second and third argument of `FileLocator::__construct`
+ * removed loading resources from `%kernel.root_dir%/Resources` and `%kernel.root_dir%` as
+ fallback directories.
+ * removed class `ExceptionListener`, use `ErrorListener` instead
+
4.4.0
-----
diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php b/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php
index 675c584234ef3..270f690e5b839 100644
--- a/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php
+++ b/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php
@@ -20,8 +20,6 @@ interface CacheClearerInterface
{
/**
* Clears any caches necessary.
- *
- * @param string $cacheDir The cache directory
*/
- public function clear($cacheDir);
+ public function clear(string $cacheDir);
}
diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php b/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php
index 5061a8d1815e3..95d41a8db6ba5 100644
--- a/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php
+++ b/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php
@@ -30,7 +30,7 @@ public function __construct(iterable $clearers = [])
/**
* {@inheritdoc}
*/
- public function clear($cacheDir)
+ public function clear(string $cacheDir)
{
foreach ($this->clearers as $clearer) {
$clearer->clear($cacheDir);
diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php
index 47a6ece5c1e0e..ab37853d3121a 100644
--- a/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php
+++ b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php
@@ -23,12 +23,12 @@ public function __construct(array $pools = [])
$this->pools = $pools;
}
- public function hasPool($name)
+ public function hasPool(string $name)
{
return isset($this->pools[$name]);
}
- public function getPool($name)
+ public function getPool(string $name)
{
if (!$this->hasPool($name)) {
throw new \InvalidArgumentException(sprintf('Cache pool not found: %s.', $name));
@@ -37,7 +37,7 @@ public function getPool($name)
return $this->pools[$name];
}
- public function clearPool($name)
+ public function clearPool(string $name)
{
if (!isset($this->pools[$name])) {
throw new \InvalidArgumentException(sprintf('Cache pool not found: %s.', $name));
@@ -49,7 +49,7 @@ public function clearPool($name)
/**
* {@inheritdoc}
*/
- public function clear($cacheDir)
+ public function clear(string $cacheDir)
{
foreach ($this->pools as $pool) {
$pool->clear();
diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php
index 52dc2ad2c3d83..aef42d62f4265 100644
--- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php
+++ b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php
@@ -18,7 +18,7 @@
*/
abstract class CacheWarmer implements CacheWarmerInterface
{
- protected function writeCacheFile($file, $content)
+ protected function writeCacheFile(string $file, $content)
{
$tmpFile = @tempnam(\dirname($file), basename($file));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php
index 9d84f03d82da3..1b3d73b7e16d5 100644
--- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php
+++ b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php
@@ -45,10 +45,8 @@ public function enableOnlyOptionalWarmers()
/**
* Warms up the cache.
- *
- * @param string $cacheDir The cache directory
*/
- public function warmUp($cacheDir)
+ public function warmUp(string $cacheDir)
{
if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
$collectedLogs = [];
diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php b/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php
index 25d8ee8f61a8c..e7715a33f6c99 100644
--- a/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php
+++ b/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php
@@ -20,8 +20,6 @@ interface WarmableInterface
{
/**
* Warms up the cache.
- *
- * @param string $cacheDir The cache directory
*/
- public function warmUp($cacheDir);
+ public function warmUp(string $cacheDir);
}
diff --git a/src/Symfony/Component/HttpKernel/Client.php b/src/Symfony/Component/HttpKernel/Client.php
deleted file mode 100644
index 77c74a5ad2e3f..0000000000000
--- a/src/Symfony/Component/HttpKernel/Client.php
+++ /dev/null
@@ -1,201 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel;
-
-use Symfony\Component\BrowserKit\AbstractBrowser;
-use Symfony\Component\BrowserKit\CookieJar;
-use Symfony\Component\BrowserKit\History;
-use Symfony\Component\BrowserKit\Request as DomRequest;
-use Symfony\Component\BrowserKit\Response as DomResponse;
-use Symfony\Component\HttpFoundation\File\UploadedFile;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-
-/**
- * Client simulates a browser and makes requests to an HttpKernel instance.
- *
- * @method Request getRequest() A Request instance
- * @method Response getResponse() A Response instance
- *
- * @deprecated since Symfony 4.3, use HttpKernelBrowser instead.
- */
-class Client extends AbstractBrowser
-{
- protected $kernel;
- private $catchExceptions = true;
-
- /**
- * @param array $server The server parameters (equivalent of $_SERVER)
- */
- public function __construct(HttpKernelInterface $kernel, array $server = [], History $history = null, CookieJar $cookieJar = null)
- {
- // These class properties must be set before calling the parent constructor, as it may depend on it.
- $this->kernel = $kernel;
- $this->followRedirects = false;
-
- parent::__construct($server, $history, $cookieJar);
- }
-
- /**
- * Sets whether to catch exceptions when the kernel is handling a request.
- *
- * @param bool $catchExceptions Whether to catch exceptions
- */
- public function catchExceptions($catchExceptions)
- {
- $this->catchExceptions = $catchExceptions;
- }
-
- /**
- * Makes a request.
- *
- * @return Response A Response instance
- */
- protected function doRequest($request)
- {
- $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $this->catchExceptions);
-
- if ($this->kernel instanceof TerminableInterface) {
- $this->kernel->terminate($request, $response);
- }
-
- return $response;
- }
-
- /**
- * Returns the script to execute when the request must be insulated.
- *
- * @return string
- */
- protected function getScript($request)
- {
- $kernel = var_export(serialize($this->kernel), true);
- $request = var_export(serialize($request), true);
-
- $errorReporting = error_reporting();
-
- $requires = '';
- foreach (get_declared_classes() as $class) {
- if (0 === strpos($class, 'ComposerAutoloaderInit')) {
- $r = new \ReflectionClass($class);
- $file = \dirname($r->getFileName(), 2).'/autoload.php';
- if (file_exists($file)) {
- $requires .= 'require_once '.var_export($file, true).";\n";
- }
- }
- }
-
- if (!$requires) {
- throw new \RuntimeException('Composer autoloader not found.');
- }
-
- $code = <<getHandleScript();
- }
-
- protected function getHandleScript()
- {
- return <<<'EOF'
-$response = $kernel->handle($request);
-
-if ($kernel instanceof Symfony\Component\HttpKernel\TerminableInterface) {
- $kernel->terminate($request, $response);
-}
-
-echo serialize($response);
-EOF;
- }
-
- /**
- * Converts the BrowserKit request to a HttpKernel request.
- *
- * @return Request A Request instance
- */
- protected function filterRequest(DomRequest $request)
- {
- $httpRequest = Request::create($request->getUri(), $request->getMethod(), $request->getParameters(), $request->getCookies(), $request->getFiles(), $request->getServer(), $request->getContent());
-
- foreach ($this->filterFiles($httpRequest->files->all()) as $key => $value) {
- $httpRequest->files->set($key, $value);
- }
-
- return $httpRequest;
- }
-
- /**
- * Filters an array of files.
- *
- * This method created test instances of UploadedFile so that the move()
- * method can be called on those instances.
- *
- * If the size of a file is greater than the allowed size (from php.ini) then
- * an invalid UploadedFile is returned with an error set to UPLOAD_ERR_INI_SIZE.
- *
- * @see UploadedFile
- *
- * @return array An array with all uploaded files marked as already moved
- */
- protected function filterFiles(array $files)
- {
- $filtered = [];
- foreach ($files as $key => $value) {
- if (\is_array($value)) {
- $filtered[$key] = $this->filterFiles($value);
- } elseif ($value instanceof UploadedFile) {
- if ($value->isValid() && $value->getSize() > UploadedFile::getMaxFilesize()) {
- $filtered[$key] = new UploadedFile(
- '',
- $value->getClientOriginalName(),
- $value->getClientMimeType(),
- UPLOAD_ERR_INI_SIZE,
- true
- );
- } else {
- $filtered[$key] = new UploadedFile(
- $value->getPathname(),
- $value->getClientOriginalName(),
- $value->getClientMimeType(),
- $value->getError(),
- true
- );
- }
- }
- }
-
- return $filtered;
- }
-
- /**
- * Converts the HttpKernel response to a BrowserKit response.
- *
- * @return DomResponse A DomResponse instance
- */
- protected function filterResponse($response)
- {
- // this is needed to support StreamedResponse
- ob_start();
- $response->sendContent();
- $content = ob_get_clean();
-
- return new DomResponse($content, $response->getStatusCode(), $response->headers->all());
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Config/FileLocator.php b/src/Symfony/Component/HttpKernel/Config/FileLocator.php
index 03dfe3de316af..6eca98635c570 100644
--- a/src/Symfony/Component/HttpKernel/Config/FileLocator.php
+++ b/src/Symfony/Component/HttpKernel/Config/FileLocator.php
@@ -23,68 +23,24 @@ class FileLocator extends BaseFileLocator
{
private $kernel;
- /**
- * @deprecated since Symfony 4.4
- */
- private $path;
-
- public function __construct(KernelInterface $kernel/*, string $path = null, array $paths = [], bool $triggerDeprecation = true*/)
+ public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
- if (2 <= \func_num_args()) {
- $this->path = func_get_arg(1);
- $paths = 3 <= \func_num_args() ? func_get_arg(2) : [];
- if (null !== $this->path) {
- $paths[] = $this->path;
- }
-
- if (4 !== \func_num_args() || func_get_arg(3)) {
- @trigger_error(sprintf('Passing more than one argument to %s is deprecated since Symfony 4.4 and will be removed in 5.0.', __METHOD__), E_USER_DEPRECATED);
- }
- } else {
- $paths = [];
- }
-
- parent::__construct($paths);
+ parent::__construct();
}
/**
* {@inheritdoc}
*/
- public function locate($file, $currentPath = null, $first = true)
+ public function locate(string $file, string $currentPath = null, bool $first = true)
{
if (isset($file[0]) && '@' === $file[0]) {
- return $this->kernel->locateResource($file, $this->path, $first, false);
- }
-
- $locations = parent::locate($file, $currentPath, $first);
-
- if (isset($file[0]) && !(
- '/' === $file[0] || '\\' === $file[0]
- || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && ('\\' === $file[2] || '/' === $file[2]))
- || null !== parse_url($file, PHP_URL_SCHEME)
- )) {
- $deprecation = false;
-
- // no need to trigger deprecations when the loaded file is given as absolute path
- foreach ($this->paths as $deprecatedPath) {
- foreach ((array) $locations as $location) {
- if (null !== $currentPath && 0 === strpos($location, $currentPath)) {
- return $locations;
- }
-
- if (0 === strpos($location, $deprecatedPath) && (null === $currentPath || false === strpos($location, $currentPath))) {
- $deprecation = sprintf('Loading the file "%s" from the global resource directory "%s" is deprecated since Symfony 4.4 and will be removed in 5.0.', $file, $deprecatedPath);
- }
- }
- }
+ $resource = $this->kernel->locateResource($file);
- if ($deprecation) {
- @trigger_error($deprecation, E_USER_DEPRECATED);
- }
+ return $first ? $resource : [$resource];
}
- return $locations;
+ return parent::locate($file, $currentPath, $first);
}
}
diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
index 89154ece716f0..43bbe178d5694 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
@@ -43,7 +43,7 @@ public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFa
/**
* {@inheritdoc}
*/
- public function getArguments(Request $request, $controller): array
+ public function getArguments(Request $request, callable $controller): array
{
$arguments = [];
diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolverInterface.php
index ba97775a90797..2c32492cf4e81 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolverInterface.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolverInterface.php
@@ -24,11 +24,9 @@ interface ArgumentResolverInterface
/**
* Returns the arguments to pass to the controller.
*
- * @param callable $controller
- *
* @return array An array of arguments to pass to the controller
*
* @throws \RuntimeException When no value could be provided for a required argument
*/
- public function getArguments(Request $request, $controller);
+ public function getArguments(Request $request, callable $controller);
}
diff --git a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php
index 015eea91fa5f0..447df4ae8ffc6 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php
@@ -32,7 +32,7 @@ public function __construct(ContainerInterface $container, LoggerInterface $logg
parent::__construct($logger);
}
- protected function createController($controller)
+ protected function createController(string $controller)
{
if (1 === substr_count($controller, ':')) {
$controller = str_replace(':', '::', $controller);
@@ -45,7 +45,7 @@ protected function createController($controller)
/**
* {@inheritdoc}
*/
- protected function instantiateController($class)
+ protected function instantiateController(string $class)
{
$class = ltrim($class, '\\');
diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
index 4bf258416f192..4534de43ba652 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
@@ -98,13 +98,11 @@ public function getController(Request $request)
/**
* Returns a callable for the given controller.
*
- * @param string $controller A Controller string
- *
* @return callable A PHP callable
*
* @throws \InvalidArgumentException When the controller cannot be created
*/
- protected function createController($controller)
+ protected function createController(string $controller)
{
if (false === strpos($controller, '::')) {
$controller = $this->instantiateController($controller);
@@ -142,11 +140,9 @@ protected function createController($controller)
/**
* Returns an instantiated controller.
*
- * @param string $class A class name
- *
* @return object
*/
- protected function instantiateController($class)
+ protected function instantiateController(string $class)
{
return new $class();
}
diff --git a/src/Symfony/Component/HttpKernel/Controller/TraceableArgumentResolver.php b/src/Symfony/Component/HttpKernel/Controller/TraceableArgumentResolver.php
index 39848127b76cf..e22cf082c4e27 100644
--- a/src/Symfony/Component/HttpKernel/Controller/TraceableArgumentResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/TraceableArgumentResolver.php
@@ -31,7 +31,7 @@ public function __construct(ArgumentResolverInterface $resolver, Stopwatch $stop
/**
* {@inheritdoc}
*/
- public function getArguments(Request $request, $controller)
+ public function getArguments(Request $request, callable $controller)
{
$e = $this->stopwatch->start('controller.get_arguments');
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php
index 356ce227e8993..7b38ed5d79324 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php
@@ -19,16 +19,11 @@
*
* @author Bart van den Burg
*
- * @final since Symfony 4.4
+ * @final
*/
class AjaxDataCollector extends DataCollector
{
- /**
- * {@inheritdoc}
- *
- * @param \Throwable|null $exception
- */
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
// all collecting is done client side
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php
index 266d97626f732..1e435cd21dcb4 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php
@@ -20,7 +20,7 @@
/**
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class ConfigDataCollector extends DataCollector implements LateDataCollectorInterface
{
@@ -28,21 +28,6 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte
* @var KernelInterface
*/
private $kernel;
- private $name;
- private $version;
-
- public function __construct(string $name = null, string $version = null)
- {
- if (1 <= \func_num_args()) {
- @trigger_error(sprintf('The "$name" argument in method "%s()" is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- }
- if (2 <= \func_num_args()) {
- @trigger_error(sprintf('The "$version" argument in method "%s()" is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
- }
-
- $this->name = $name;
- $this->version = $version;
- }
/**
* Sets the Kernel associated with this Request.
@@ -54,14 +39,10 @@ public function setKernel(KernelInterface $kernel = null)
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
$this->data = [
- 'app_name' => $this->name,
- 'app_version' => $this->version,
'token' => $response->headers->get('X-Debug-Token'),
'symfony_version' => Kernel::VERSION,
'symfony_state' => 'unknown',
@@ -111,26 +92,6 @@ public function lateCollect()
$this->data = $this->cloneVar($this->data);
}
- /**
- * @deprecated since Symfony 4.2
- */
- public function getApplicationName()
- {
- @trigger_error(sprintf('The method "%s()" is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->data['app_name'];
- }
-
- /**
- * @deprecated since Symfony 4.2
- */
- public function getApplicationVersion()
- {
- @trigger_error(sprintf('The method "%s()" is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->data['app_version'];
- }
-
/**
* Gets the token.
*
@@ -246,20 +207,6 @@ public function getPhpTimezone()
return $this->data['php_timezone'];
}
- /**
- * Gets the application name.
- *
- * @return string The application name
- *
- * @deprecated since Symfony 4.2
- */
- public function getAppName()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
-
- return 'n/a';
- }
-
/**
* Gets the environment.
*
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
index aaf557438481b..ccaf66da0438f 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php
@@ -38,29 +38,6 @@ abstract class DataCollector implements DataCollectorInterface
*/
private $cloner;
- /**
- * @deprecated since Symfony 4.3, store all the serialized state in the data property instead
- */
- public function serialize()
- {
- @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3, store all the serialized state in the data property instead.', __METHOD__), E_USER_DEPRECATED);
-
- $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
- $isCalledFromOverridingMethod = isset($trace[1]['function'], $trace[1]['object']) && 'serialize' === $trace[1]['function'] && $this === $trace[1]['object'];
-
- return $isCalledFromOverridingMethod ? $this->data : serialize($this->data);
- }
-
- /**
- * @deprecated since Symfony 4.3, store all the serialized state in the data property instead
- */
- public function unserialize($data)
- {
- @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3, store all the serialized state in the data property instead.', __METHOD__), E_USER_DEPRECATED);
-
- $this->data = \is_array($data) ? $data : unserialize($data);
- }
-
/**
* Converts the variable into a serializable Data instance.
*
@@ -112,19 +89,24 @@ protected function getCasters()
*/
public function __sleep()
{
- if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) {
- @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3, store all the serialized state in the "data" property instead.', $c), E_USER_DEPRECATED);
- $this->data = $this->serialize();
- }
-
return ['data'];
}
public function __wakeup()
{
- if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'unserialize'))->getDeclaringClass()->name) {
- @trigger_error(sprintf('Implementing the "%s::unserialize()" method is deprecated since Symfony 4.3, store all the serialized state in the "data" property instead.', $c), E_USER_DEPRECATED);
- $this->unserialize($this->data);
- }
+ }
+
+ /**
+ * @internal to prevent implementing \Serializable
+ */
+ final protected function serialize()
+ {
+ }
+
+ /**
+ * @internal to prevent implementing \Serializable
+ */
+ final protected function unserialize($data)
+ {
}
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php
index a302ad3009572..30ab7cc70c280 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php
@@ -24,10 +24,8 @@ interface DataCollectorInterface extends ResetInterface
{
/**
* Collects data for the given Request and Response.
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/);
+ public function collect(Request $request, Response $response, \Throwable $exception = null);
/**
* Returns the name of the collector.
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
index 6ed9abea5b880..b3288b563a566 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php
@@ -26,7 +26,7 @@
/**
* @author Nicolas Grekas
*
- * @final since Symfony 4.3
+ * @final
*/
class DumpDataCollector extends DataCollector implements DataDumperInterface
{
@@ -98,12 +98,7 @@ public function dump(Data $data)
}
}
- /**
- * {@inheritdoc}
- *
- * @param \Throwable|null $exception
- */
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
if (!$this->dataCount) {
$this->data = [];
@@ -185,12 +180,12 @@ public function __wakeup()
self::__construct($this->stopwatch, $fileLinkFormat, $charset);
}
- public function getDumpsCount()
+ public function getDumpsCount(): int
{
return $this->dataCount;
}
- public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1)
+ public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1): array
{
$data = fopen('php://memory', 'r+b');
@@ -217,7 +212,7 @@ public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1)
return $dumps;
}
- public function getName()
+ public function getName(): string
{
return 'dump';
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php
index 89fd18338688f..27930fea09728 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
-use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
@@ -24,7 +23,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class EventDataCollector extends DataCollector implements LateDataCollectorInterface
{
@@ -40,10 +39,8 @@ public function __construct(EventDispatcherInterface $dispatcher = null, Request
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
$this->currentRequest = $this->requestStack && $this->requestStack->getMasterRequest() !== $request ? $request : null;
$this->data = [
@@ -64,12 +61,9 @@ public function reset()
public function lateCollect()
{
- if ($this->dispatcher instanceof TraceableEventDispatcherInterface) {
+ if ($this->dispatcher instanceof TraceableEventDispatcher) {
$this->setCalledListeners($this->dispatcher->getCalledListeners($this->currentRequest));
$this->setNotCalledListeners($this->dispatcher->getNotCalledListeners($this->currentRequest));
- }
-
- if ($this->dispatcher instanceof TraceableEventDispatcher) {
$this->setOrphanedEvents($this->dispatcher->getOrphanedEvents($this->currentRequest));
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php
index 222cae5d2d24c..5ff13f71b8c8d 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php
@@ -20,19 +20,15 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class ExceptionDataCollector extends DataCollector
{
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
- $exception = 2 < \func_num_args() ? func_get_arg(2) : null;
-
if (null !== $exception) {
$this->data = [
'exception' => FlattenException::createFromThrowable($exception),
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
index 9314e432e150f..f8dc8859efd42 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
@@ -22,7 +22,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
{
@@ -43,10 +43,8 @@ public function __construct($logger = null, string $containerPathPrefix = null,
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
$this->currentRequest = $this->requestStack && $this->requestStack->getMasterRequest() !== $request ? $request : null;
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php
index 7ffcdab41dca4..37302128ad674 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php
@@ -19,7 +19,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class MemoryDataCollector extends DataCollector implements LateDataCollectorInterface
{
@@ -30,10 +30,8 @@ public function __construct()
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
$this->updateMemoryUsage();
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
index edfa6dee4cc93..9585c130f080a 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
@@ -16,14 +16,14 @@
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\ControllerEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class RequestDataCollector extends DataCollector implements EventSubscriberInterface, LateDataCollectorInterface
{
@@ -36,10 +36,8 @@ public function __construct()
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
// attributes are serialized and as they can be anything, they need to be converted to strings.
$attributes = [];
@@ -352,18 +350,12 @@ public function getForwardToken()
return isset($this->data['forward_token']) ? $this->data['forward_token'] : null;
}
- /**
- * @final since Symfony 4.3
- */
- public function onKernelController(FilterControllerEvent $event)
+ public function onKernelController(ControllerEvent $event)
{
$this->controllers[$event->getRequest()] = $event->getController();
}
- /**
- * @final since Symfony 4.3
- */
- public function onKernelResponse(FilterResponseEvent $event)
+ public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php
index 5f12392330883..5ed697048bc8d 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php
@@ -14,7 +14,7 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
+use Symfony\Component\HttpKernel\Event\ControllerEvent;
/**
* @author Fabien Potencier
@@ -34,11 +34,9 @@ public function __construct()
/**
* {@inheritdoc}
*
- * @param \Throwable|null $exception
- *
- * @final since Symfony 4.4
+ * @final
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
if ($response instanceof RedirectResponse) {
$this->data['redirect'] = true;
@@ -70,10 +68,8 @@ protected function guessRoute(Request $request, $controller)
/**
* Remembers the controller associated to each request.
- *
- * @final since Symfony 4.3
*/
- public function onKernelController(FilterControllerEvent $event)
+ public function onKernelController(ControllerEvent $event)
{
$this->controllers[$event->getRequest()] = $event->getController();
}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
index 7c0cdaa90d7d1..4e95603fb81c4 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
@@ -20,7 +20,7 @@
/**
* @author Fabien Potencier
*
- * @final since Symfony 4.4
+ * @final
*/
class TimeDataCollector extends DataCollector implements LateDataCollectorInterface
{
@@ -35,10 +35,8 @@ public function __construct(KernelInterface $kernel = null, Stopwatch $stopwatch
/**
* {@inheritdoc}
- *
- * @param \Throwable|null $exception
*/
- public function collect(Request $request, Response $response/*, \Throwable $exception = null*/)
+ public function collect(Request $request, Response $response, \Throwable $exception = null)
{
if (null !== $this->kernel) {
$startTime = $this->kernel->getStartTime();
diff --git a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
index ccde50abd3058..74abb818626df 100644
--- a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
+++ b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
@@ -20,7 +20,7 @@
*
* @author Jérémy Romey
*
- * @final since Symfony 4.3
+ * @final
*/
class FileLinkFormatter
{
@@ -46,7 +46,7 @@ public function __construct($fileLinkFormat = null, RequestStack $requestStack =
$this->urlFormat = $urlFormat;
}
- public function format($file, $line)
+ public function format(string $file, int $line)
{
if ($fmt = $this->getFileLinkFormat()) {
for ($i = 1; isset($fmt[$i]); ++$i) {
@@ -75,7 +75,7 @@ public function __sleep(): array
/**
* @internal
*/
- public static function generateUrlFormat(UrlGeneratorInterface $router, $routeName, $queryString)
+ public static function generateUrlFormat(UrlGeneratorInterface $router, string $routeName, string $queryString): ?string
{
try {
return $router->generate($routeName).$queryString;
diff --git a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
index ce4ddb35d3f75..1ece493f4e708 100644
--- a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
+++ b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
@@ -26,7 +26,7 @@ class TraceableEventDispatcher extends BaseTraceableEventDispatcher
/**
* {@inheritdoc}
*/
- protected function beforeDispatch(string $eventName, $event)
+ protected function beforeDispatch(string $eventName, object $event)
{
switch ($eventName) {
case KernelEvents::REQUEST:
@@ -60,7 +60,7 @@ protected function beforeDispatch(string $eventName, $event)
/**
* {@inheritdoc}
*/
- protected function afterDispatch(string $eventName, $event)
+ protected function afterDispatch(string $eventName, object $event)
{
switch ($eventName) {
case KernelEvents::CONTROLLER_ARGUMENTS:
diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php b/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php
index 526c11faacda2..2ee6737319337 100644
--- a/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php
+++ b/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php
@@ -35,7 +35,7 @@ public function __construct(ContainerInterface $container, RequestStack $request
/**
* {@inheritdoc}
*/
- public function render($uri, $renderer = 'inline', array $options = [])
+ public function render($uri, string $renderer = 'inline', array $options = [])
{
if (!isset($this->initialized[$renderer]) && $this->container->has($renderer)) {
$this->addRenderer($this->container->get($renderer));
diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php
index a3f5012e3268f..f7ddb870f1816 100644
--- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php
+++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php
@@ -53,6 +53,13 @@ public function process(ContainerBuilder $container)
$parameterBag = $container->getParameterBag();
$controllers = [];
+ $publicAliases = [];
+ foreach ($container->getAliases() as $id => $alias) {
+ if ($alias->isPublic()) {
+ $publicAliases[(string) $alias][] = $id;
+ }
+ }
+
foreach ($container->findTaggedServiceIds($this->controllerTag, true) as $id => $tags) {
$def = $container->getDefinition($id);
$def->setPublic(true);
@@ -170,15 +177,22 @@ public function process(ContainerBuilder $container)
$message .= ' Did you forget to add a use statement?';
}
- throw new InvalidArgumentException($message);
- }
+ $container->register($erroredId = '.errored.'.$container->hash($message), $type)
+ ->addError($message);
- $target = ltrim($target, '\\');
- $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, $p->name) : new Reference($target, $invalidBehavior);
+ $args[$p->name] = new Reference($erroredId, ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE);
+ } else {
+ $target = ltrim($target, '\\');
+ $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, $p->name) : new Reference($target, $invalidBehavior);
+ }
}
// register the maps as a per-method service-locators
if ($args) {
$controllers[$id.'::'.$r->name] = ServiceLocatorTagPass::register($container, $args);
+
+ foreach ($publicAliases[$id] ?? [] as $alias) {
+ $controllers[$alias.'::'.$r->name] = clone $controllers[$id.'::'.$r->name];
+ }
}
}
}
diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php
index 596b6188f66cb..714c9fd9258be 100644
--- a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php
+++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php
@@ -43,6 +43,11 @@ public function process(ContainerBuilder $container)
// any methods listed for call-at-instantiation cannot be actions
$reason = false;
list($id, $action) = explode('::', $controller);
+
+ if ($container->hasAlias($id)) {
+ continue;
+ }
+
$controllerDef = $container->getDefinition($id);
foreach ($controllerDef->getMethodCalls() as list($method)) {
if (0 === strcasecmp($action, $method)) {
@@ -51,9 +56,6 @@ public function process(ContainerBuilder $container)
}
}
if (!$reason) {
- // Deprecated since Symfony 4.1. See Symfony\Component\HttpKernel\Controller\ContainerControllerResolver
- $controllers[$id.':'.$action] = $argumentRef;
-
if ('__invoke' === $action) {
$controllers[$id] = $argumentRef;
}
diff --git a/src/Symfony/Component/HttpKernel/Event/ControllerArgumentsEvent.php b/src/Symfony/Component/HttpKernel/Event/ControllerArgumentsEvent.php
index 5efb80cf8f44f..d075ee90bd52b 100644
--- a/src/Symfony/Component/HttpKernel/Event/ControllerArgumentsEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/ControllerArgumentsEvent.php
@@ -11,6 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
/**
* Allows filtering of controller arguments.
*
@@ -22,9 +25,37 @@
* controller.
*
* @author Christophe Coevoet
- *
- * @final since Symfony 4.4
*/
-class ControllerArgumentsEvent extends FilterControllerArgumentsEvent
+final class ControllerArgumentsEvent extends KernelEvent
{
+ private $controller;
+ private $arguments;
+
+ public function __construct(HttpKernelInterface $kernel, callable $controller, array $arguments, Request $request, ?int $requestType)
+ {
+ parent::__construct($kernel, $request, $requestType);
+
+ $this->controller = $controller;
+ $this->arguments = $arguments;
+ }
+
+ public function getController(): callable
+ {
+ return $this->controller;
+ }
+
+ public function setController(callable $controller)
+ {
+ $this->controller = $controller;
+ }
+
+ public function getArguments(): array
+ {
+ return $this->arguments;
+ }
+
+ public function setArguments(array $arguments)
+ {
+ $this->arguments = $arguments;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php b/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php
index 7b642eaa33532..da88800e20285 100644
--- a/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php
@@ -11,6 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
/**
* Allows filtering of a controller callable.
*
@@ -21,9 +24,25 @@
* Controllers should be callables.
*
* @author Bernhard Schussek
- *
- * @final since Symfony 4.4
*/
-class ControllerEvent extends FilterControllerEvent
+final class ControllerEvent extends KernelEvent
{
+ private $controller;
+
+ public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, ?int $requestType)
+ {
+ parent::__construct($kernel, $request, $requestType);
+
+ $this->setController($controller);
+ }
+
+ public function getController(): callable
+ {
+ return $this->controller;
+ }
+
+ public function setController(callable $controller): void
+ {
+ $this->controller = $controller;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php b/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php
index 3dae0d4ce69a2..a18fbd31f4949 100644
--- a/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php
@@ -11,6 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
/**
* Allows to create a response for a thrown exception.
*
@@ -18,14 +21,56 @@
* current request. The propagation of this event is stopped as soon as a
* response is set.
*
- * You can also call setException() to replace the thrown exception. This
+ * You can also call setThrowable() to replace the thrown exception. This
* exception will be thrown if no response is set during processing of this
* event.
*
* @author Bernhard Schussek
- *
- * @final since Symfony 4.4
*/
-class ExceptionEvent extends GetResponseForExceptionEvent
+final class ExceptionEvent extends RequestEvent
{
+ private $throwable;
+
+ /**
+ * @var bool
+ */
+ private $allowCustomResponseCode = false;
+
+ public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, \Throwable $e)
+ {
+ parent::__construct($kernel, $request, $requestType);
+
+ $this->setThrowable($e);
+ }
+
+ public function getThrowable(): \Throwable
+ {
+ return $this->throwable;
+ }
+
+ /**
+ * Replaces the thrown exception.
+ *
+ * This exception will be thrown if no response is set in the event.
+ */
+ public function setThrowable(\Throwable $exception): void
+ {
+ $this->throwable = $exception;
+ }
+
+ /**
+ * Mark the event as allowing a custom response code.
+ */
+ public function allowCustomResponseCode(): void
+ {
+ $this->allowCustomResponseCode = true;
+ }
+
+ /**
+ * Returns true if the event allows a custom response code.
+ */
+ public function isAllowingCustomResponseCode(): bool
+ {
+ return $this->allowCustomResponseCode;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php
deleted file mode 100644
index f3c5dc34aa50a..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php
+++ /dev/null
@@ -1,43 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * @deprecated since Symfony 4.3, use ControllerArgumentsEvent instead
- */
-class FilterControllerArgumentsEvent extends FilterControllerEvent
-{
- private $arguments;
-
- public function __construct(HttpKernelInterface $kernel, callable $controller, array $arguments, Request $request, ?int $requestType)
- {
- parent::__construct($kernel, $controller, $request, $requestType);
-
- $this->arguments = $arguments;
- }
-
- /**
- * @return array
- */
- public function getArguments()
- {
- return $this->arguments;
- }
-
- public function setArguments(array $arguments)
- {
- $this->arguments = $arguments;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php
deleted file mode 100644
index 74fa681f80984..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * @deprecated since Symfony 4.3, use ControllerEvent instead
- */
-class FilterControllerEvent extends KernelEvent
-{
- private $controller;
-
- public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, ?int $requestType)
- {
- parent::__construct($kernel, $request, $requestType);
-
- $this->setController($controller);
- }
-
- /**
- * Returns the current controller.
- *
- * @return callable
- */
- public function getController()
- {
- return $this->controller;
- }
-
- public function setController(callable $controller)
- {
- $this->controller = $controller;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php
deleted file mode 100644
index eaa2e8256540e..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php
+++ /dev/null
@@ -1,49 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * @deprecated since Symfony 4.3, use ResponseEvent instead
- */
-class FilterResponseEvent extends KernelEvent
-{
- private $response;
-
- public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, Response $response)
- {
- parent::__construct($kernel, $request, $requestType);
-
- $this->setResponse($response);
- }
-
- /**
- * Returns the current response object.
- *
- * @return Response
- */
- public function getResponse()
- {
- return $this->response;
- }
-
- /**
- * Sets a new response object.
- */
- public function setResponse(Response $response)
- {
- $this->response = $response;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php b/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php
index 9374d2db954ed..306a7ad1cd6ed 100644
--- a/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php
@@ -15,9 +15,7 @@
* Triggered whenever a request is fully processed.
*
* @author Benjamin Eberlei
- *
- * @final since Symfony 4.4
*/
-class FinishRequestEvent extends KernelEvent
+final class FinishRequestEvent extends KernelEvent
{
}
diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php
deleted file mode 100644
index fbed7beef570f..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php
+++ /dev/null
@@ -1,52 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\HttpFoundation\Response;
-
-/**
- * @deprecated since Symfony 4.3, use RequestEvent instead
- */
-class GetResponseEvent extends KernelEvent
-{
- private $response;
-
- /**
- * Returns the response object.
- *
- * @return Response|null
- */
- public function getResponse()
- {
- return $this->response;
- }
-
- /**
- * Sets a response and stops event propagation.
- */
- public function setResponse(Response $response)
- {
- $this->response = $response;
-
- $this->stopPropagation();
- }
-
- /**
- * Returns whether a response was set.
- *
- * @return bool Whether a response was set
- */
- public function hasResponse()
- {
- return null !== $this->response;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php
deleted file mode 100644
index 4e70dbc63e53e..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php
+++ /dev/null
@@ -1,55 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * @deprecated since Symfony 4.3, use ViewEvent instead
- */
-class GetResponseForControllerResultEvent extends RequestEvent
-{
- /**
- * The return value of the controller.
- *
- * @var mixed
- */
- private $controllerResult;
-
- public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, $controllerResult)
- {
- parent::__construct($kernel, $request, $requestType);
-
- $this->controllerResult = $controllerResult;
- }
-
- /**
- * Returns the return value of the controller.
- *
- * @return mixed The controller return value
- */
- public function getControllerResult()
- {
- return $this->controllerResult;
- }
-
- /**
- * Assigns the return value of the controller.
- *
- * @param mixed $controllerResult The controller return value
- */
- public function setControllerResult($controllerResult)
- {
- $this->controllerResult = $controllerResult;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php
deleted file mode 100644
index 8e2b183136e95..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php
+++ /dev/null
@@ -1,91 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\Debug\Exception\FatalThrowableError;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * @deprecated since Symfony 4.3, use ExceptionEvent instead
- */
-class GetResponseForExceptionEvent extends RequestEvent
-{
- private $throwable;
- private $exception;
- private $allowCustomResponseCode = false;
-
- public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, \Throwable $e)
- {
- parent::__construct($kernel, $request, $requestType);
-
- $this->setThrowable($e);
- }
-
- public function getThrowable(): \Throwable
- {
- return $this->throwable;
- }
-
- /**
- * Replaces the thrown exception.
- *
- * This exception will be thrown if no response is set in the event.
- */
- public function setThrowable(\Throwable $exception): void
- {
- $this->exception = null;
- $this->throwable = $exception;
- }
-
- /**
- * @deprecated since Symfony 4.4, use getThrowable instead
- *
- * @return \Exception The thrown exception
- */
- public function getException()
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.4, use "getThrowable()" instead.', __METHOD__), E_USER_DEPRECATED);
-
- return $this->exception ?? $this->exception = $this->throwable instanceof \Exception ? $this->throwable : new FatalThrowableError($this->throwable);
- }
-
- /**
- * @deprecated since Symfony 4.4, use setThrowable instead
- *
- * @param \Exception $exception The thrown exception
- */
- public function setException(\Exception $exception)
- {
- @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.4, use "setThrowable()" instead.', __METHOD__), E_USER_DEPRECATED);
-
- $this->throwable = $this->exception = $exception;
- }
-
- /**
- * Mark the event as allowing a custom response code.
- */
- public function allowCustomResponseCode()
- {
- $this->allowCustomResponseCode = true;
- }
-
- /**
- * Returns true if the event allows a custom response code.
- *
- * @return bool
- */
- public function isAllowingCustomResponseCode()
- {
- return $this->allowCustomResponseCode;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php
index f6dff06ac855e..08558d533ae1e 100644
--- a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Contracts\EventDispatcher\Event;
/**
* Base class for events thrown in the HttpKernel component.
diff --git a/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php
deleted file mode 100644
index b86bf07742412..0000000000000
--- a/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php
+++ /dev/null
@@ -1,41 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\Event;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * @deprecated since Symfony 4.3, use TerminateEvent instead
- */
-class PostResponseEvent extends KernelEvent
-{
- private $response;
-
- public function __construct(HttpKernelInterface $kernel, Request $request, Response $response)
- {
- parent::__construct($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
-
- $this->response = $response;
- }
-
- /**
- * Returns the response for which this event was thrown.
- *
- * @return Response
- */
- public function getResponse()
- {
- return $this->response;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/Event/RequestEvent.php b/src/Symfony/Component/HttpKernel/Event/RequestEvent.php
index c1beb929f31ea..0b2b98eeba48b 100644
--- a/src/Symfony/Component/HttpKernel/Event/RequestEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/RequestEvent.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Response;
+
/**
* Allows to create a response for a request.
*
@@ -20,6 +22,37 @@
*
* @author Bernhard Schussek
*/
-class RequestEvent extends GetResponseEvent
+class RequestEvent extends KernelEvent
{
+ private $response;
+
+ /**
+ * Returns the response object.
+ *
+ * @return Response|null
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Sets a response and stops event propagation.
+ */
+ public function setResponse(Response $response)
+ {
+ $this->response = $response;
+
+ $this->stopPropagation();
+ }
+
+ /**
+ * Returns whether a response was set.
+ *
+ * @return bool Whether a response was set
+ */
+ public function hasResponse()
+ {
+ return null !== $this->response;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/Event/ResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/ResponseEvent.php
index eae8c39cc3358..1e56ebea2c633 100644
--- a/src/Symfony/Component/HttpKernel/Event/ResponseEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/ResponseEvent.php
@@ -11,6 +11,10 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
/**
* Allows to filter a Response object.
*
@@ -19,9 +23,25 @@
* browser.
*
* @author Bernhard Schussek
- *
- * @final since Symfony 4.4
*/
-class ResponseEvent extends FilterResponseEvent
+final class ResponseEvent extends KernelEvent
{
+ private $response;
+
+ public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, Response $response)
+ {
+ parent::__construct($kernel, $request, $requestType);
+
+ $this->setResponse($response);
+ }
+
+ public function getResponse(): Response
+ {
+ return $this->response;
+ }
+
+ public function setResponse(Response $response): void
+ {
+ $this->response = $response;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/Event/TerminateEvent.php b/src/Symfony/Component/HttpKernel/Event/TerminateEvent.php
index 6a74445d6781d..e0002fb56fb99 100644
--- a/src/Symfony/Component/HttpKernel/Event/TerminateEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/TerminateEvent.php
@@ -11,6 +11,10 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
/**
* Allows to execute logic after a response was sent.
*
@@ -18,9 +22,20 @@
* will always return the value of `HttpKernelInterface::MASTER_REQUEST`.
*
* @author Jordi Boggiano
- *
- * @final since Symfony 4.4
*/
-class TerminateEvent extends PostResponseEvent
+final class TerminateEvent extends KernelEvent
{
+ private $response;
+
+ public function __construct(HttpKernelInterface $kernel, Request $request, Response $response)
+ {
+ parent::__construct($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
+
+ $this->response = $response;
+ }
+
+ public function getResponse(): Response
+ {
+ return $this->response;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/Event/ViewEvent.php b/src/Symfony/Component/HttpKernel/Event/ViewEvent.php
index da50da82a9fcd..66a4ceab9317d 100644
--- a/src/Symfony/Component/HttpKernel/Event/ViewEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/ViewEvent.php
@@ -11,6 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
/**
* Allows to create a response for the return value of a controller.
*
@@ -19,9 +22,40 @@
* response is set.
*
* @author Bernhard Schussek
- *
- * @final since Symfony 4.4
*/
-class ViewEvent extends GetResponseForControllerResultEvent
+final class ViewEvent extends RequestEvent
{
+ /**
+ * The return value of the controller.
+ *
+ * @var mixed
+ */
+ private $controllerResult;
+
+ public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, $controllerResult)
+ {
+ parent::__construct($kernel, $request, $requestType);
+
+ $this->controllerResult = $controllerResult;
+ }
+
+ /**
+ * Returns the return value of the controller.
+ *
+ * @return mixed The controller return value
+ */
+ public function getControllerResult()
+ {
+ return $this->controllerResult;
+ }
+
+ /**
+ * Assigns the return value of the controller.
+ *
+ * @param mixed $controllerResult The controller return value
+ */
+ public function setControllerResult($controllerResult): void
+ {
+ $this->controllerResult = $controllerResult;
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
index 0a6789d85ae2e..871ffc61d5612 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
@@ -15,9 +15,10 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
+use Symfony\Component\HttpKernel\Exception\UnexpectedSessionUsageException;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -33,7 +34,7 @@
* @author Johannes M. Schmitt
* @author Tobias Schultze
*
- * @internal since Symfony 4.3
+ * @internal
*/
abstract class AbstractSessionListener implements EventSubscriberInterface
{
@@ -41,13 +42,15 @@ abstract class AbstractSessionListener implements EventSubscriberInterface
protected $container;
private $sessionUsageStack = [];
+ private $debug;
- public function __construct(ContainerInterface $container = null)
+ public function __construct(ContainerInterface $container = null, bool $debug = false)
{
$this->container = $container;
+ $this->debug = $debug;
}
- public function onKernelRequest(GetResponseEvent $event)
+ public function onKernelRequest(RequestEvent $event)
{
if (!$event->isMasterRequest()) {
return;
@@ -67,7 +70,7 @@ public function onKernelRequest(GetResponseEvent $event)
$this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : 0;
}
- public function onKernelResponse(FilterResponseEvent $event)
+ public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
@@ -82,16 +85,6 @@ public function onKernelResponse(FilterResponseEvent $event)
return;
}
- if ($session instanceof Session ? $session->getUsageIndex() !== end($this->sessionUsageStack) : $session->isStarted()) {
- if ($autoCacheControl) {
- $response
- ->setExpires(new \DateTime())
- ->setPrivate()
- ->setMaxAge(0)
- ->headers->addCacheControlDirective('must-revalidate');
- }
- }
-
if ($session->isStarted()) {
/*
* Saves the session, in case it is still open, before sending the response/headers.
@@ -120,11 +113,32 @@ public function onKernelResponse(FilterResponseEvent $event)
*/
$session->save();
}
+
+ if ($session instanceof Session ? $session->getUsageIndex() === end($this->sessionUsageStack) : !$session->isStarted()) {
+ return;
+ }
+
+ if ($autoCacheControl) {
+ $response
+ ->setExpires(new \DateTime())
+ ->setPrivate()
+ ->setMaxAge(0)
+ ->headers->addCacheControlDirective('must-revalidate');
+ }
+
+ if (!$event->getRequest()->attributes->get('_stateless', false)) {
+ return;
+ }
+
+ if ($this->debug) {
+ throw new UnexpectedSessionUsageException('Session was used while the request was declared stateless.');
+ }
+
+ if ($this->container->has('logger')) {
+ $this->container->get('logger')->warning('Session was used while the request was declared stateless.');
+ }
}
- /**
- * @internal
- */
public function onFinishRequest(FinishRequestEvent $event)
{
if ($event->isMasterRequest()) {
@@ -132,7 +146,7 @@ public function onFinishRequest(FinishRequestEvent $event)
}
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 128],
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
index 86f179add761b..19d13b8c46742 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
@@ -15,8 +15,8 @@
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -27,7 +27,7 @@
* @author Bulat Shakirzyanov
* @author Fabien Potencier
*
- * @internal since Symfony 4.3
+ * @internal
*/
abstract class AbstractTestSessionListener implements EventSubscriberInterface
{
@@ -39,7 +39,7 @@ public function __construct(array $sessionOptions = [])
$this->sessionOptions = $sessionOptions;
}
- public function onKernelRequest(GetResponseEvent $event)
+ public function onKernelRequest(RequestEvent $event)
{
if (!$event->isMasterRequest()) {
return;
@@ -62,7 +62,7 @@ public function onKernelRequest(GetResponseEvent $event)
* Checks if session was initialized and saves if current request is master
* Runs on 'kernel.response' in test environment.
*/
- public function onKernelResponse(FilterResponseEvent $event)
+ public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
@@ -97,7 +97,7 @@ public function onKernelResponse(FilterResponseEvent $event)
}
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 192],
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php b/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php
index 47c7069c9eadc..9e896adb310ee 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -20,7 +20,7 @@
*
* @author Gildas Quemener
*
- * @final since Symfony 4.3
+ * @final
*/
class AddRequestFormatsListener implements EventSubscriberInterface
{
@@ -34,7 +34,7 @@ public function __construct(array $formats)
/**
* Adds request formats.
*/
- public function onKernelRequest(GetResponseEvent $event)
+ public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
foreach ($this->formats as $format => $mimeTypes) {
@@ -45,7 +45,7 @@ public function onKernelRequest(GetResponseEvent $event)
/**
* {@inheritdoc}
*/
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [KernelEvents::REQUEST => ['onKernelRequest', 100]];
}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
index dcb54ea891c4a..5efd7621712e6 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
@@ -15,9 +15,7 @@
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
-use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\ErrorHandler\ErrorHandler;
-use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\HttpKernel\Event\KernelEvent;
@@ -28,7 +26,7 @@
*
* @author Nicolas Grekas
*
- * @final since Symfony 4.4
+ * @final
*/
class DebugHandlersListener implements EventSubscriberInterface
{
@@ -64,7 +62,7 @@ public function __construct(callable $exceptionHandler = null, LoggerInterface $
/**
* Configures the error handler.
*/
- public function configure(Event $event = null)
+ public function configure(object $event = null)
{
if ($event instanceof ConsoleEvent && !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
return;
@@ -145,7 +143,7 @@ public function configure(Event $event = null)
}
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
$events = [KernelEvents::REQUEST => ['configure', 2048]];
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php
index 26c361f754e53..6192a452e30d6 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php
@@ -14,11 +14,11 @@
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\FlattenException as LegacyFlattenException;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
@@ -33,7 +33,7 @@ class ErrorListener implements EventSubscriberInterface
protected $logger;
protected $debug;
- public function __construct($controller, LoggerInterface $logger = null, $debug = false)
+ public function __construct($controller, LoggerInterface $logger = null, bool $debug = false)
{
$this->controller = $controller;
$this->logger = $logger;
@@ -47,7 +47,7 @@ public function logKernelException(ExceptionEvent $event)
$this->logException($event->getThrowable(), sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', $e->getClass(), $e->getMessage(), $e->getFile(), $e->getLine()));
}
- public function onKernelException(ExceptionEvent $event, string $eventName = null, EventDispatcherInterface $eventDispatcher = null)
+ public function onKernelException(ExceptionEvent $event)
{
if (null === $this->controller) {
return;
@@ -79,12 +79,15 @@ public function onKernelException(ExceptionEvent $event, string $eventName = nul
$event->setResponse($response);
- if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) {
- $cspRemovalListener = function ($event) use (&$cspRemovalListener, $eventDispatcher) {
- $event->getResponse()->headers->remove('Content-Security-Policy');
- $eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener);
- };
- $eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128);
+ if ($this->debug) {
+ $event->getRequest()->attributes->set('_remove_csp_headers', true);
+ }
+ }
+
+ public function removeCspHeader(ResponseEvent $event): void
+ {
+ if ($this->debug && $event->getRequest()->attributes->get('_remove_csp_headers', false)) {
+ $event->getResponse()->headers->remove('Content-Security-Policy');
}
}
@@ -114,6 +117,7 @@ public static function getSubscribedEvents(): array
['logKernelException', 0],
['onKernelException', -128],
],
+ KernelEvents::RESPONSE => ['removeCspHeader', -128],
];
}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
deleted file mode 100644
index aa4349428ad12..0000000000000
--- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
+++ /dev/null
@@ -1,136 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\EventListener;
-
-use Psr\Log\LoggerInterface;
-use Symfony\Component\ErrorHandler\Exception\FlattenException;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
-use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "ErrorListener" instead.', ExceptionListener::class), E_USER_DEPRECATED);
-
-/**
- * @deprecated since Symfony 4.4, use ErrorListener instead
- */
-class ExceptionListener implements EventSubscriberInterface
-{
- protected $controller;
- protected $logger;
- protected $debug;
-
- public function __construct($controller, LoggerInterface $logger = null, $debug = false)
- {
- $this->controller = $controller;
- $this->logger = $logger;
- $this->debug = $debug;
- }
-
- public function logKernelException(GetResponseForExceptionEvent $event)
- {
- $e = FlattenException::createFromThrowable($event->getException());
-
- $this->logException($event->getException(), sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', $e->getClass(), $e->getMessage(), $e->getFile(), $e->getLine()));
- }
-
- public function onKernelException(GetResponseForExceptionEvent $event)
- {
- if (null === $this->controller) {
- return;
- }
-
- $exception = $event->getException();
- $request = $this->duplicateRequest($exception, $event->getRequest());
- $eventDispatcher = \func_num_args() > 2 ? func_get_arg(2) : null;
-
- try {
- $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false);
- } catch (\Exception $e) {
- $f = FlattenException::createFromThrowable($e);
-
- $this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', $f->getClass(), $f->getMessage(), $e->getFile(), $e->getLine()));
-
- $prev = $e;
- do {
- if ($exception === $wrapper = $prev) {
- throw $e;
- }
- } while ($prev = $wrapper->getPrevious());
-
- $prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
- $prev->setAccessible(true);
- $prev->setValue($wrapper, $exception);
-
- throw $e;
- }
-
- $event->setResponse($response);
-
- if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) {
- $cspRemovalListener = function ($event) use (&$cspRemovalListener, $eventDispatcher) {
- $event->getResponse()->headers->remove('Content-Security-Policy');
- $eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener);
- };
- $eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128);
- }
- }
-
- public static function getSubscribedEvents()
- {
- return [
- KernelEvents::EXCEPTION => [
- ['logKernelException', 0],
- ['onKernelException', -128],
- ],
- ];
- }
-
- /**
- * Logs an exception.
- *
- * @param \Exception $exception The \Exception instance
- * @param string $message The error message to log
- */
- protected function logException(\Exception $exception, $message)
- {
- if (null !== $this->logger) {
- if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
- $this->logger->critical($message, ['exception' => $exception]);
- } else {
- $this->logger->error($message, ['exception' => $exception]);
- }
- }
- }
-
- /**
- * Clones the request for the exception.
- *
- * @return Request The cloned request
- */
- protected function duplicateRequest(\Exception $exception, Request $request)
- {
- $attributes = [
- '_controller' => $this->controller,
- 'exception' => FlattenException::createFromThrowable($exception),
- 'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null,
- ];
- $request = $request->duplicate(null, null, $attributes);
- $request->setMethod('GET');
-
- return $request;
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php
index 5ae61daaaee01..14c6aa63d1f83 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php
@@ -13,7 +13,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\UriSigner;
@@ -29,7 +29,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class FragmentListener implements EventSubscriberInterface
{
@@ -50,7 +50,7 @@ public function __construct(UriSigner $signer, string $fragmentPath = '/_fragmen
*
* @throws AccessDeniedHttpException if the request does not come from a trusted IP
*/
- public function onKernelRequest(GetResponseEvent $event)
+ public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
@@ -83,15 +83,14 @@ protected function validateRequest(Request $request)
}
// is the Request signed?
- // we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
- if ($this->signer->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().(null !== ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''))) {
+ if ($this->signer->checkRequest($request)) {
return;
}
throw new AccessDeniedHttpException();
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => [['onKernelRequest', 48]],
diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php
index b09a6c76a2241..8037e32c0e90e 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php
@@ -15,8 +15,8 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\KernelEvent;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RequestContextAwareInterface;
@@ -25,7 +25,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class LocaleListener implements EventSubscriberInterface
{
@@ -45,7 +45,7 @@ public function setDefaultLocale(KernelEvent $event)
$event->getRequest()->setDefaultLocale($this->defaultLocale);
}
- public function onKernelRequest(GetResponseEvent $event)
+ public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
@@ -74,7 +74,7 @@ private function setRouterContext(Request $request)
}
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => [
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
index b8464f1627353..3143f2b9a60b7 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
@@ -14,9 +14,9 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
-use Symfony\Component\HttpKernel\Event\PostResponseEvent;
+use Symfony\Component\HttpKernel\Event\ExceptionEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
+use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Profiler\Profiler;
@@ -25,7 +25,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class ProfilerListener implements EventSubscriberInterface
{
@@ -56,7 +56,7 @@ public function __construct(Profiler $profiler, RequestStack $requestStack, Requ
/**
* Handles the onKernelException event.
*/
- public function onKernelException(GetResponseForExceptionEvent $event)
+ public function onKernelException(ExceptionEvent $event)
{
if ($this->onlyMasterRequests && !$event->isMasterRequest()) {
return;
@@ -68,7 +68,7 @@ public function onKernelException(GetResponseForExceptionEvent $event)
/**
* Handles the onKernelResponse event.
*/
- public function onKernelResponse(FilterResponseEvent $event)
+ public function onKernelResponse(ResponseEvent $event)
{
$master = $event->isMasterRequest();
if ($this->onlyMasterRequests && !$master) {
@@ -96,7 +96,7 @@ public function onKernelResponse(FilterResponseEvent $event)
$this->parents[$request] = $this->requestStack->getParentRequest();
}
- public function onKernelTerminate(PostResponseEvent $event)
+ public function onKernelTerminate(TerminateEvent $event)
{
// attach children to parents
foreach ($this->profiles as $request) {
@@ -116,7 +116,7 @@ public function onKernelTerminate(PostResponseEvent $event)
$this->parents = new \SplObjectStorage();
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => ['onKernelResponse', -100],
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php b/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php
index 01973e222a097..d8292aec43e49 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -20,7 +20,7 @@
*
* @author Fabien Potencier
*
- * @final since Symfony 4.3
+ * @final
*/
class ResponseListener implements EventSubscriberInterface
{
@@ -34,7 +34,7 @@ public function __construct(string $charset)
/**
* Filters the Response.
*/
- public function onKernelResponse(FilterResponseEvent $event)
+ public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
@@ -49,7 +49,7 @@ public function onKernelResponse(FilterResponseEvent $event)
$response->prepare($event->getRequest());
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => 'onKernelResponse',
diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php
index ee88debae45e8..31bbc3b047267 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php
@@ -16,9 +16,9 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -38,7 +38,7 @@
* @author Fabien Potencier
* @author Yonel Ceruto
*
- * @final since Symfony 4.3
+ * @final
*/
class RouterListener implements EventSubscriberInterface
{
@@ -94,7 +94,7 @@ public function onKernelFinishRequest(FinishRequestEvent $event)
$this->setCurrentRequest($this->requestStack->getParentRequest());
}
- public function onKernelRequest(GetResponseEvent $event)
+ public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
@@ -141,7 +141,7 @@ public function onKernelRequest(GetResponseEvent $event)
}
}
- public function onKernelException(GetResponseForExceptionEvent $event)
+ public function onKernelException(ExceptionEvent $event)
{
if (!$this->debug || !($e = $event->getThrowable()) instanceof NotFoundHttpException) {
return;
@@ -152,7 +152,7 @@ public function onKernelException(GetResponseForExceptionEvent $event)
}
}
- public static function getSubscribedEvents()
+ public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => [['onKernelRequest', 32]],
diff --git a/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php
deleted file mode 100644
index 3b105ced34902..0000000000000
--- a/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpKernel\EventListener;
-
-@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.1, use AbstractSessionListener instead.', SaveSessionListener::class), E_USER_DEPRECATED);
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
-
-/**
- * @author Tobias Schultze
- *
- * @deprecated since Symfony 4.1, use AbstractSessionListener instead
- */
-class SaveSessionListener implements EventSubscriberInterface
-{
- public function onKernelResponse(FilterResponseEvent $event)
- {
- if (!$event->isMasterRequest()) {
- return;
- }
-
- $request = $event->getRequest();
- if ($request->hasSession() && ($session = $request->getSession())->isStarted()) {
- $session->save();
- }
- }
-
- public static function getSubscribedEvents()
- {
- return [
- // low priority but higher than StreamedResponseListener
- KernelEvents::RESPONSE => [['onKernelResponse', -1000]],
- ];
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php
index a53ade797cdac..e982a795b2d3f 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php
@@ -28,9 +28,9 @@
*/
class SessionListener extends AbstractSessionListener
{
- public function __construct(ContainerInterface $container)
+ public function __construct(ContainerInterface $container, bool $debug = false)
{
- $this->container = $container;
+ parent::__construct($container, $debug);
}
protected function getSession(): ?SessionInterface
diff --git a/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php b/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php
index f28f5d868a2bf..730ee5453f63c 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php
@@ -13,7 +13,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -22,14 +22,14 @@
*
* @author Fabien Potencier