diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.md b/.github/ISSUE_TEMPLATE/1_Bug_report.md
deleted file mode 100644
index aef16611e0f77..0000000000000
--- a/.github/ISSUE_TEMPLATE/1_Bug_report.md
+++ /dev/null
@@ -1,22 +0,0 @@
----
-name: 🐛 Bug Report
-about: ⚠️ See below for security reports
-labels: Bug
-
----
-
-**Symfony version(s) affected**: x.y.z
-
-**Description**
-
-
-**How to reproduce**
-
-
-**Possible Solution**
-
-
-**Additional context**
-
diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.yaml b/.github/ISSUE_TEMPLATE/1_Bug_report.yaml
index 5518d4e4ad79d..ef0f72c794278 100644
--- a/.github/ISSUE_TEMPLATE/1_Bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/1_Bug_report.yaml
@@ -1,5 +1,5 @@
name: 🐛 Bug Report
-description: ⚠️ See below for security reports
+description: ⚠️ NEVER report security issues, read https://symfony.com/security instead
labels: Bug
body:
@@ -14,7 +14,7 @@ body:
id: description
attributes:
label: Description
- description: A clear and consise description of the problem
+ description: A clear and concise description of the problem
validations:
required: true
- type: textarea
@@ -22,15 +22,21 @@ body:
attributes:
label: How to reproduce
description: |
- Code and/or config needed to reproduce the problem.
- If it's a complex bug, create a "bug reproducer" as explained in https://symfony.com/doc/current/contributing/code/reproducer.html
+ ⚠️ This is the most important part of the report ⚠️
+ Without a way to easily reproduce your issue, there is little chance we will be able to help you and work on a fix.
+ Please, take the time to show us some code and/or config that is needed for others to reproduce the problem easily.
+ Most of the time, creating a "bug reproducer" as explained in the URL below is the best way to help us
+ and increases the chances someone will have a look at it:
+ https://symfony.com/doc/current/contributing/code/reproducer.html
validations:
required: true
- type: textarea
id: possible-solution
attributes:
label: Possible Solution
- description: "Optional: only if you have suggestions on a fix/reason for the bug"
+ description: |
+ Optional: only if you have suggestions on a fix/reason for the bug
+ Don't hesitate to create a pull request with your solution, it helps get faster feedback.
- type: textarea
id: additional-context
attributes:
diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.md b/.github/ISSUE_TEMPLATE/2_Feature_request.md
deleted file mode 100644
index 908c5ee52664d..0000000000000
--- a/.github/ISSUE_TEMPLATE/2_Feature_request.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-name: 🚀 Feature Request
-about: RFC and ideas for new features and improvements
-
----
-
-**Description**
-
-
-**Example**
-
diff --git a/.github/ISSUE_TEMPLATE/3_Support_question.md b/.github/ISSUE_TEMPLATE/3_Support_question.md
deleted file mode 100644
index 9480710c15655..0000000000000
--- a/.github/ISSUE_TEMPLATE/3_Support_question.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-name: ⛔ Support Question
-about: See https://symfony.com/support for questions about using Symfony and its components
-
----
-
-We use GitHub issues only to discuss about Symfony bugs and new features. For
-this kind of questions about using Symfony or third-party bundles, please use
-any of the support alternatives shown in https://symfony.com/support
-
-Thanks!
diff --git a/.github/ISSUE_TEMPLATE/4_Documentation_issue.md b/.github/ISSUE_TEMPLATE/4_Documentation_issue.md
deleted file mode 100644
index 0855c3c5f1e12..0000000000000
--- a/.github/ISSUE_TEMPLATE/4_Documentation_issue.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-name: ⛔ Documentation Issue
-about: See https://github.com/symfony/symfony-docs/issues for documentation issues
-
----
-
-Symfony Documentation has its own dedicated repository. Please open your
-documentation-related issue at https://github.com/symfony/symfony-docs/issues
-
-Thanks!
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000000..34227566ed84a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Support Question
+ url: https://symfony.com/support
+ about: We use GitHub issues only to discuss about Symfony bugs and new features. For this kind of questions about using Symfony or third-party bundles, please use any of the support alternatives shown in https://symfony.com/support
+ - name: Documentation Issue
+ url: https://github.com/symfony/symfony-docs/issues
+ about: Symfony Documentation has its own dedicated repository.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 62662f876fd3a..5e7092d385910 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
-| Branch? | 5.4 for features / 4.4 or 5.3 for bug fixes
+| Branch? | 6.1 for features / 4.4, 5.3, 5.4 or 6.0 for bug fixes
| Bug fix? | yes/no
| New feature? | yes/no
| Deprecations? | yes/no
@@ -8,14 +8,14 @@
| License | MIT
| Doc PR | symfony/symfony-docs#...
diff --git a/.github/get-modified-packages.php b/.github/get-modified-packages.php
index 9135a1da666e5..a3682af0b9d1a 100644
--- a/.github/get-modified-packages.php
+++ b/.github/get-modified-packages.php
@@ -17,9 +17,33 @@
return strlen($b) <=> strlen($a) ?: $a <=> $b;
});
-function isComponentBridge(string $packageDir): bool
+function getPackageType(string $packageDir): string
{
- return 0 < preg_match('@Symfony/Component/.*/Bridge/@', $packageDir);
+ if (preg_match('@Symfony/Bridge/@', $packageDir)) {
+ return 'bridge';
+ }
+
+ if (preg_match('@Symfony/Bundle/@', $packageDir)) {
+ return 'bundle';
+ }
+
+ if (preg_match('@Symfony/Component/[^/]+/Bridge/@', $packageDir)) {
+ return 'component_bridge';
+ }
+
+ if (preg_match('@Symfony/Component/@', $packageDir)) {
+ return 'component';
+ }
+
+ if (preg_match('@Symfony/Contracts/@', $packageDir)) {
+ return 'contract';
+ }
+
+ if (preg_match('@Symfony/Contracts$@', $packageDir)) {
+ return 'contracts';
+ }
+
+ throw new \LogicException();
}
$newPackage = [];
@@ -43,7 +67,7 @@ function isComponentBridge(string $packageDir): bool
$output = [];
foreach ($modifiedPackages as $directory => $bool) {
$name = json_decode(file_get_contents($directory.'/composer.json'), true)['name'] ?? 'unknown';
- $output[] = ['name' => $name, 'directory' => $directory, 'new' => $newPackage[$directory] ?? false, 'component_bridge' => isComponentBridge($directory)];
+ $output[] = ['name' => $name, 'directory' => $directory, 'new' => $newPackage[$directory] ?? false, 'type' => getPackageType($directory)];
}
echo json_encode($output);
diff --git a/.github/workflows/package-tests.yml b/.github/workflows/package-tests.yml
index cb66e2d8d3b03..3c0a7c36be89f 100644
--- a/.github/workflows/package-tests.yml
+++ b/.github/workflows/package-tests.yml
@@ -63,13 +63,18 @@ jobs:
DIR=$(_jq '.directory')
NAME=$(_jq '.name')
echo "::group::$NAME"
+ TYPE=$(_jq '.type')
localExit=0
- _file_exist $DIR/.gitattributes || localExit=1
+ if [ $TYPE != 'contract' ] && [ $TYPE != 'contracts' ]; then
+ _file_exist $DIR/.gitattributes || localExit=1
+ fi
_file_exist $DIR/.gitignore || localExit=1
_file_exist $DIR/CHANGELOG.md || localExit=1
_file_exist $DIR/LICENSE || localExit=1
- _file_exist $DIR/phpunit.xml.dist || localExit=1
+ if [ $TYPE != 'contract' ]; then
+ _file_exist $DIR/phpunit.xml.dist || localExit=1
+ fi
_file_exist $DIR/README.md || localExit=1
_file_not_exist $DIR/phpunit.xml || localExit=1
@@ -77,7 +82,7 @@ jobs:
echo "Verifying new package"
_correct_license_file $DIR/LICENSE || localExit=1
- if [ $(_jq '.component_bridge') == false ]; then
+ if [ $TYPE == 'component_bridge' ]; then
if [ ! $(cat composer.json | jq -e ".replace.\"$NAME\"|test(\"self.version\")") ]; then
echo "Composer.json's replace section needs to contain $NAME"
localExit=1
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index c60d92ba61cce..c10c70de1abbc 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -21,10 +21,10 @@ jobs:
matrix:
include:
- php: '7.2'
- - php: '8.1'
- php: '7.4'
- mode: high-deps
- php: '8.0'
+ mode: high-deps
+ - php: '8.1'
mode: low-deps
- php: '8.2'
mode: experimental
@@ -174,19 +174,21 @@ jobs:
exit 0
fi
- (cd src/Symfony/Component/HttpFoundation; cp composer.json composer.bak; composer require --dev --no-update mongodb/mongodb)
-
if [[ "${{ matrix.mode }}" = low-deps ]]; then
echo "$COMPONENTS" | xargs -n1 | parallel -j +3 "_run_tests {} 'cd {} && $COMPOSER_UP --prefer-lowest --prefer-stable && $PHPUNIT'"
exit 0
fi
+ (cd src/Symfony/Component/HttpFoundation; cp composer.json composer.bak; composer require --dev --no-update mongodb/mongodb)
+ (cd src/Symfony/Component/Lock; cp composer.json composer.bak; composer require --dev --no-update mongodb/mongodb)
+
# matrix.mode = high-deps
echo "$COMPONENTS" | xargs -n1 | parallel -j +3 "_run_tests {} 'cd {} && $COMPOSER_UP && $PHPUNIT$LEGACY'" || X=1
# get a list of the patched components (relies on .github/build-packages.php being called in the previous step)
(cd src/Symfony/Component/HttpFoundation; mv composer.bak composer.json)
+ (cd src/Symfony/Component/Lock; mv composer.bak composer.json)
PATCHED_COMPONENTS=$(git diff --name-only src/ | grep composer.json || true)
# for 5.4 LTS, checkout and test previous major with the patched components (only for patched components)
@@ -200,6 +202,7 @@ jobs:
git checkout -m FETCH_HEAD
PATCHED_COMPONENTS=$(echo "$PATCHED_COMPONENTS" | xargs dirname | xargs -n1 -I{} bash -c "[ -e '{}/phpunit.xml.dist' ] && echo '{}'" | sort || true)
(cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb)
+ (cd src/Symfony/Component/Lock; composer require --dev --no-update mongodb/mongodb)
if [[ $PATCHED_COMPONENTS ]]; then
echo "::group::install phpunit"
./phpunit install
diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md
index 5ba6dab3b6129..5cc4204a52f35 100644
--- a/CHANGELOG-5.4.md
+++ b/CHANGELOG-5.4.md
@@ -7,6 +7,30 @@ in 5.4 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.4.0...v5.4.1
+* 5.4.1 (2021-12-09)
+
+ * bug #44490 [DependencyInjection][Messenger] Add auto-registration for BatchHandlerInterface (GaryPEGEOT)
+ * bug #44523 [Console] Fix polyfill-php73 requirement (Seldaek)
+ * bug #44502 [HttpFoundation] do not call preg_match() on null (xabbuh)
+ * bug #44475 [Console] Handle alias in completion script (GromNaN)
+ * bug #44481 [FrameworkBundle] Fix loginUser() causing deprecation (wouterj)
+ * bug #44416 [Translation] Make http requests synchronous when reading the Loco API (Kocal)
+ * bug #44437 [HttpKernel] Fix wrong usage of SessionUtils::popSessionCookie (simonchrz)
+ * bug #44350 [Translation] Fix TranslationTrait (Tomasz Kusy)
+ * bug #44460 [SecurityBundle] Fix ambiguous deprecation message on missing provider (chalasr)
+ * bug #44467 [Console] Fix parameter types for `ProcessHelper::mustRun()` (derrabus)
+ * bug #44427 [FrameworkBundle] Fix compatibility with symfony/security-core 6.x (deps=high tests) (wouterj)
+ * bug #44424 [SecurityBundle] Don't rely on deprecated strategy constants (derrabus)
+ * bug #44399 Prevent infinite nesting of lazy `ObjectManager` instances when `ObjectManager` is reset (Ocramius)
+ * bug #44395 [HttpKernel] fix sending Vary: Accept-Language when appropriate (nicolas-grekas)
+ * bug #44385 [DependencyInjection] Skip parameter attribute configurators in AttributeAutoconfigurationPass if we can't get the constructor reflector (fancyweb)
+ * bug #44359 Avoid duplicated session listener registration in tests (alexander-schranz)
+ * bug #44375 [DoctrineBridge] fix calling get_class on non-object (kbond)
+ * bug #44378 [HttpFoundation] fix SessionHandlerFactory using connections (dmaicher)
+ * bug #44365 [SecurityBundle] Fix invalid reference with `always_authenticate_before_granting` (chalasr)
+ * bug #44361 [HttpClient] Fix handling error info in MockResponse (fancyweb)
+ * bug #44370 [Lock] create lock table if it does not exist (martinssipenko)
+
* 5.4.0 (2021-11-29)
* bug #44309 [Messenger] Leverage DBAL's getNativeConnection() method (derrabus)
diff --git a/UPGRADE-5.4.md b/UPGRADE-5.4.md
index a2ce35807705d..d7492d23f3cfb 100644
--- a/UPGRADE-5.4.md
+++ b/UPGRADE-5.4.md
@@ -38,6 +38,7 @@ FrameworkBundle
* Deprecate `get()`, `has()`, `getDoctrine()`, and `dispatchMessage()` in `AbstractController`, use method/constructor injection instead
* Deprecate the `cache.adapter.doctrine` service: The Doctrine Cache library is deprecated. Either switch to Symfony Cache or use the PSR-6 adapters provided by Doctrine Cache.
* In `framework.cache` configuration, using `cache.adapter.pdo` adapter with a Doctrine DBAL connection is deprecated, use `cache.adapter.doctrine_dbal` instead.
+ * Deprecate not setting the `framework.messenger.reset_on_message` config option, its default value will change to `true` in 6.0
HttpKernel
----------
@@ -62,13 +63,12 @@ Messenger
* Deprecate not setting the `delete_after_ack` config option (or DSN parameter) using the Redis transport,
its default value will change to `true` in 6.0
- * Deprecate not setting the `reset_on_message` config option, its default value will change to `true` in 6.0
Monolog
-------
* Deprecate `ResetLoggersWorkerSubscriber` to reset buffered logs in messenger
- workers, use "reset_on_message" option in messenger configuration instead.
+ workers, use `framework.messenger.reset_on_message` option in FrameworkBundle messenger configuration instead.
SecurityBundle
--------------
@@ -170,3 +170,19 @@ Security
* Deprecate passing the strategy as string to `AccessDecisionManager`,
pass an instance of `AccessDecisionStrategyInterface` instead
* Flag `AccessDecisionManager` as `@final`
+ * Deprecate passing `$credentials` to `PreAuthenticatedToken`,
+ `SwitchUserToken` and `UsernamePasswordToken`:
+
+ Before:
+ ```php
+ $token = new UsernamePasswordToken($user, $credentials, $firewallName, $roles);
+ $token = new PreAuthenticatedToken($user, $credentials, $firewallName, $roles);
+ $token = new SwitchUserToken($user, $credentials, $firewallName, $roles, $originalToken);
+ ```
+
+ After:
+ ```php
+ $token = new UsernamePasswordToken($user, $firewallName, $roles);
+ $token = new PreAuthenticatedToken($user, $firewallName, $roles);
+ $token = new SwitchUserToken($user, $firewallName, $roles, $originalToken);
+ ```
diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md
index 10c9dcc9199e3..347263738c7b4 100644
--- a/UPGRADE-6.0.md
+++ b/UPGRADE-6.0.md
@@ -124,7 +124,6 @@ HttpFoundation
* Removed the `Request::HEADER_X_FORWARDED_ALL` constant, use either `Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO` or `Request::HEADER_X_FORWARDED_AWS_ELB` or `Request::HEADER_X_FORWARDED_TRAEFIK`constants instead
* Rename `RequestStack::getMasterRequest()` to `getMainRequest()`
* Not passing `FILTER_REQUIRE_ARRAY` or `FILTER_FORCE_ARRAY` flags to `InputBag::filter()` when filtering an array will throw `BadRequestException`
- * Removed the `Request::HEADER_X_FORWARDED_ALL` constant
* Retrieving non-scalar values using `InputBag::get()` will throw `BadRequestException` (use `InputBad::all()` instead to retrieve an array)
* Passing non-scalar default value as the second argument `InputBag::get()` will throw `\InvalidArgumentException`
* Passing non-scalar, non-array value as the second argument `InputBag::set()` will throw `\InvalidArgumentException`
@@ -173,7 +172,6 @@ Messenger
* Removed the `prefetch_count` parameter in the AMQP bridge.
* Removed the use of TLS option for Redis Bridge, use `rediss://127.0.0.1` instead of `redis://127.0.0.1?tls=1`
* The `delete_after_ack` config option of the Redis transport now defaults to `true`
- * The `reset_on_message` config option now defaults to `true`
Mime
----
@@ -408,6 +406,22 @@ Security
```
* `AccessDecisionManager` does not accept strings as strategy anymore,
pass an instance of `AccessDecisionStrategyInterface` instead
+ * Removed the `$credentials` argument of `PreAuthenticatedToken`,
+ `SwitchUserToken` and `UsernamePasswordToken`:
+
+ Before:
+ ```php
+ $token = new UsernamePasswordToken($user, $credentials, $firewallName, $roles);
+ $token = new PreAuthenticatedToken($user, $credentials, $firewallName, $roles);
+ $token = new SwitchUserToken($user, $credentials, $firewallName, $roles, $originalToken);
+ ```
+
+ After:
+ ```php
+ $token = new UsernamePasswordToken($user, $firewallName, $roles);
+ $token = new PreAuthenticatedToken($user, $firewallName, $roles);
+ $token = new SwitchUserToken($user, $firewallName, $roles, $originalToken);
+ ```
SecurityBundle
--------------
diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php
index 7a2ad9a8d9cd0..cd88bedca1e3b 100644
--- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php
+++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php
@@ -59,7 +59,7 @@ function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) {
$name = $this->aliases[$name];
}
if (isset($this->fileMap[$name])) {
- $wrappedInstance = $this->load($this->fileMap[$name]);
+ $wrappedInstance = $this->load($this->fileMap[$name], false);
} else {
$wrappedInstance = $this->{$this->methodMap[$name]}(false);
}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php
index a004935a6afdc..dd7dabcc87db1 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php
@@ -12,8 +12,15 @@
namespace Symfony\Bridge\Doctrine\Tests;
use PHPUnit\Framework\TestCase;
+use ProxyManager\Proxy\LazyLoadingInterface;
+use ProxyManager\Proxy\ValueHolderInterface;
use Symfony\Bridge\Doctrine\ManagerRegistry;
+use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
use Symfony\Bridge\ProxyManager\Tests\LazyProxy\Dumper\PhpDumperTest;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
+use Symfony\Component\Filesystem\Filesystem;
class ManagerRegistryTest extends TestCase
{
@@ -39,6 +46,91 @@ public function testResetService()
$this->assertSame($foo, $container->get('foo'));
$this->assertObjectNotHasAttribute('bar', $foo);
}
+
+ /**
+ * When performing an entity manager lazy service reset, the reset operations may re-use the container
+ * to create a "fresh" service: when doing so, it can happen that the "fresh" service is itself a proxy.
+ *
+ * Because of that, the proxy will be populated with a wrapped value that is itself a proxy: repeating
+ * the reset operation keeps increasing this nesting until the application eventually runs into stack
+ * overflow or memory overflow operations, which can happen for long-running processes that rely on
+ * services that are reset very often.
+ */
+ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther()
+ {
+ // This test scenario only applies to containers composed as a set of generated sources
+ $this->dumpLazyServiceProjectAsFilesServiceContainer();
+
+ /** @var ContainerInterface $container */
+ $container = new \LazyServiceProjectAsFilesServiceContainer();
+
+ $registry = new TestManagerRegistry(
+ 'irrelevant',
+ [],
+ ['defaultManager' => 'foo'],
+ 'irrelevant',
+ 'defaultManager',
+ 'irrelevant'
+ );
+ $registry->setTestContainer($container);
+
+ $service = $container->get('foo');
+
+ self::assertInstanceOf(\stdClass::class, $service);
+ self::assertInstanceOf(LazyLoadingInterface::class, $service);
+ self::assertInstanceOf(ValueHolderInterface::class, $service);
+ self::assertFalse($service->isProxyInitialized());
+
+ $service->initializeProxy();
+
+ self::assertTrue($container->initialized('foo'));
+ self::assertTrue($service->isProxyInitialized());
+
+ $registry->resetManager();
+ $service->initializeProxy();
+
+ $wrappedValue = $service->getWrappedValueHolderValue();
+ self::assertInstanceOf(\stdClass::class, $wrappedValue);
+ self::assertNotInstanceOf(LazyLoadingInterface::class, $wrappedValue);
+ self::assertNotInstanceOf(ValueHolderInterface::class, $wrappedValue);
+ }
+
+ private function dumpLazyServiceProjectAsFilesServiceContainer()
+ {
+ if (class_exists(\LazyServiceProjectAsFilesServiceContainer::class, false)) {
+ return;
+ }
+
+ $container = new ContainerBuilder();
+
+ $container->register('foo', \stdClass::class)
+ ->setPublic(true)
+ ->setLazy(true);
+ $container->compile();
+
+ $fileSystem = new Filesystem();
+
+ $temporaryPath = $fileSystem->tempnam(sys_get_temp_dir(), 'symfonyManagerRegistryTest');
+ $fileSystem->remove($temporaryPath);
+ $fileSystem->mkdir($temporaryPath);
+
+ $dumper = new PhpDumper($container);
+
+ $dumper->setProxyDumper(new ProxyDumper());
+ $containerFiles = $dumper->dump([
+ 'class' => 'LazyServiceProjectAsFilesServiceContainer',
+ 'as_files' => true,
+ ]);
+
+ array_walk(
+ $containerFiles,
+ static function (string $containerSources, string $fileName) use ($temporaryPath): void {
+ (new Filesystem())->dumpFile($temporaryPath.'/'.$fileName, $containerSources);
+ }
+ );
+
+ require $temporaryPath.'/LazyServiceProjectAsFilesServiceContainer.php';
+ }
}
class TestManagerRegistry extends ManagerRegistry
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php
index ab35078f85fca..c1c698aea0ebd 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php
@@ -37,6 +37,7 @@
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
+use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
/**
@@ -859,6 +860,32 @@ public function testValidateUniquenessWithEmptyIterator($entity, $result)
$this->assertNoViolation();
}
+ public function testValueMustBeObject()
+ {
+ $constraint = new UniqueEntity([
+ 'message' => 'myMessage',
+ 'fields' => ['name'],
+ 'em' => self::EM_NAME,
+ ]);
+
+ $this->expectException(UnexpectedValueException::class);
+
+ $this->validator->validate('foo', $constraint);
+ }
+
+ public function testValueCanBeNull()
+ {
+ $constraint = new UniqueEntity([
+ 'message' => 'myMessage',
+ 'fields' => ['name'],
+ 'em' => self::EM_NAME,
+ ]);
+
+ $this->validator->validate(null, $constraint);
+
+ $this->assertNoViolation();
+ }
+
public function resultWithEmptyIterator(): array
{
$entity = new SingleIntIdEntity(1, 'foo');
diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
index c1caadd9df3dd..a151c8703dd36 100644
--- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
+++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
@@ -18,6 +18,7 @@
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
+use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Unique Entity Validator checks if one or a set of fields contain unique values.
@@ -63,6 +64,10 @@ public function validate($entity, Constraint $constraint)
return;
}
+ if (!\is_object($entity)) {
+ throw new UnexpectedValueException($entity, 'object');
+ }
+
if ($constraint->em) {
$em = $this->registry->getManager($constraint->em);
diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json
index f9772266722e6..f2e0d9520c4b4 100644
--- a/src/Symfony/Bridge/Doctrine/composer.json
+++ b/src/Symfony/Bridge/Doctrine/composer.json
@@ -53,6 +53,7 @@
},
"conflict": {
"doctrine/dbal": "<2.13.1",
+ "doctrine/lexer": "<1.1",
"doctrine/orm": "<2.7.3",
"phpunit/phpunit": "<5.4.3",
"symfony/cache": "<5.4",
@@ -61,6 +62,7 @@
"symfony/http-kernel": "<5",
"symfony/messenger": "<4.4",
"symfony/property-info": "<5",
+ "symfony/proxy-manager-bridge": "<4.4.19",
"symfony/security-bundle": "<5",
"symfony/security-core": "<5.3",
"symfony/validator": "<5.2"
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php
index 137311bd6358d..2df5b72559c64 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php
@@ -74,7 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 1;
}
- $io->success('The container was lint successfully: all services are injected with values that are compatible with their type declarations.');
+ $io->success('The container was linted successfully: all services are injected with values that are compatible with their type declarations.');
return 0;
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 9b761dbdd3934..3c46bc4609981 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -107,6 +107,7 @@
use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory;
use Symfony\Component\Messenger\Bridge\Beanstalkd\Transport\BeanstalkdTransportFactory;
use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory;
+use Symfony\Component\Messenger\Handler\BatchHandlerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\MessageBusInterface;
@@ -350,6 +351,11 @@ public function load(array $configs, ContainerBuilder $container)
$this->sessionConfigEnabled = true;
$this->registerSessionConfiguration($config['session'], $container, $loader);
+ if (!empty($config['test'])) {
+ // test listener will replace the existing session listener
+ // as we are aliasing to avoid duplicated registered events
+ $container->setAlias('session_listener', 'test.session.listener');
+ }
} elseif (!empty($config['test'])) {
$container->removeDefinition('test.session.listener');
}
@@ -577,6 +583,8 @@ public function load(array $configs, ContainerBuilder $container)
->addTag('validator.initializer');
$container->registerForAutoconfiguration(MessageHandlerInterface::class)
->addTag('messenger.message_handler');
+ $container->registerForAutoconfiguration(BatchHandlerInterface::class)
+ ->addTag('messenger.message_handler');
$container->registerForAutoconfiguration(TransportFactoryInterface::class)
->addTag('messenger.transport_factory');
$container->registerForAutoconfiguration(MimeTypeGuesserInterface::class)
diff --git a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php
index 7ae8f336b740f..8c32625b1002b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php
+++ b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php
@@ -126,7 +126,7 @@ public function loginUser(object $user, string $firewallContext = 'main'): self
$token = new TestBrowserToken($user->getRoles(), $user, $firewallContext);
// @deprecated since Symfony 5.4
- if (method_exists($token, 'isAuthenticated')) {
+ if (method_exists($token, 'setAuthenticated')) {
$token->setAuthenticated(true, false);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php
index 00b8d8aafbd5a..53613d3b5020c 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php
@@ -70,7 +70,6 @@
->args([
param('kernel.charset'),
abstract_arg('The "set_content_language_from_locale" config value'),
- param('kernel.enabled_locales'),
])
->tag('kernel.event_subscriber')
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php
index ce653c6bfaaaa..a506ac2d2915f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php
@@ -41,12 +41,6 @@ public function provideCompletionSuggestions()
private function createCommandCompletionTester(): CommandCompletionTester
{
- $dispatcher = new EventDispatcher();
- $otherDispatcher = new EventDispatcher();
-
- $dispatcher->addListener('event', ['Listener']);
- $otherDispatcher->addListener('other_event', ['OtherListener']);
-
$dispatchers = new ServiceLocator([
'event_dispatcher' => function () {
$dispatcher = new EventDispatcher();
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index 6fe970f366f3f..d64b2c38ac7e7 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -122,7 +122,7 @@ public function load(array $configs, ContainerBuilder $container)
if ($config['always_authenticate_before_granting']) {
$authorizationChecker = $container->getDefinition('security.authorization_checker');
$authorizationCheckerArgs = $authorizationChecker->getArguments();
- array_splice($authorizationCheckerArgs, 1, 0, [new Reference('security.authentication_manager')]);
+ array_splice($authorizationCheckerArgs, 1, 0, [new Reference('security.authentication.manager')]);
$authorizationChecker->setArguments($authorizationCheckerArgs);
}
@@ -704,7 +704,7 @@ private function getUserProvider(ContainerBuilder $container, string $id, array
if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey || 'custom_authenticators' === $factoryKey) {
if ('custom_authenticators' === $factoryKey) {
- trigger_deprecation('symfony/security-bundle', '5.4', 'Not configuring explicitly the provider for the "%s" listener on "%s" firewall is deprecated because it\'s ambiguous as there is more than one registered provider.', $factoryKey, $id);
+ trigger_deprecation('symfony/security-bundle', '5.4', 'Not configuring explicitly the provider for the "%s" firewall is deprecated because it\'s ambiguous as there is more than one registered provider. Set the "provider" key to one of the configured providers, even if your custom authenticators don\'t use it.', $id);
}
return 'security.user_providers';
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
index 0771f15a803b7..91e75ce0c5347 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
@@ -407,7 +407,7 @@
{% for voter_detail in decision.voter_details %}
{{ profiler_dump(voter_detail['class']) }} |
- {% if collector.voterStrategy == constant('Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager::STRATEGY_UNANIMOUS') %}
+ {% if collector.voterStrategy == 'unanimous' %}
attribute {{ voter_detail['attributes'][0] }} |
{% endif %}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
index 8747273b9e9a6..4f8c8ce127a16 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
@@ -397,7 +397,7 @@ public function testFirewallWithNoUserProviderTriggerDeprecation()
],
]);
- $this->expectDeprecation('Since symfony/security-bundle 5.4: Not configuring explicitly the provider for the "custom_authenticators" listener on "some_firewall" firewall is deprecated because it\'s ambiguous as there is more than one registered provider.');
+ $this->expectDeprecation('Since symfony/security-bundle 5.4: Not configuring explicitly the provider for the "some_firewall" firewall is deprecated because it\'s ambiguous as there is more than one registered provider. Set the "provider" key to one of the configured providers, even if your custom authenticators don\'t use it.');
$container->compile();
}
@@ -834,7 +834,7 @@ public function testLegacyAuthorizationManagerSignature()
$args = $container->getDefinition('security.authorization_checker')->getArguments();
$this->assertEquals('security.token_storage', (string) $args[0]);
- $this->assertEquals('security.authentication_manager', (string) $args[1]);
+ $this->assertEquals('security.authentication.manager', (string) $args[1]);
$this->assertEquals('security.access.decision_manager', (string) $args[2]);
$this->assertEquals('%security.access.always_authenticate_before_granting%', (string) $args[3]);
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml
index e393772ae4b21..265e1d1c83d32 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml
@@ -2,6 +2,7 @@ imports:
- { resource: ./legacy_base_config.yml }
security:
+ always_authenticate_before_granting: true
firewalls:
default:
anonymous: ~
diff --git a/src/Symfony/Component/Console/Helper/ProcessHelper.php b/src/Symfony/Component/Console/Helper/ProcessHelper.php
index 3f257dea1fa5d..87d3e65394edd 100644
--- a/src/Symfony/Component/Console/Helper/ProcessHelper.php
+++ b/src/Symfony/Component/Console/Helper/ProcessHelper.php
@@ -92,9 +92,9 @@ public function run(OutputInterface $output, $cmd, string $error = null, callabl
* This is identical to run() except that an exception is thrown if the process
* exits with a non-zero exit code.
*
- * @param string|Process $cmd An instance of Process or a command to run
- * @param callable|null $callback A PHP callback to run whenever there is some
- * output available on STDOUT or STDERR
+ * @param array|Process $cmd An instance of Process or a command to run
+ * @param callable|null $callback A PHP callback to run whenever there is some
+ * output available on STDOUT or STDERR
*
* @return Process
*
diff --git a/src/Symfony/Component/Console/Resources/completion.bash b/src/Symfony/Component/Console/Resources/completion.bash
index 971af088ba116..c5e89c3c282ab 100644
--- a/src/Symfony/Component/Console/Resources/completion.bash
+++ b/src/Symfony/Component/Console/Resources/completion.bash
@@ -9,6 +9,12 @@ _sf_{{ COMMAND_NAME }}() {
# Use newline as only separator to allow space in completion values
IFS=$'\n'
local sf_cmd="${COMP_WORDS[0]}"
+
+ # for an alias, get the real script behind it
+ if [[ $(type -t $sf_cmd) == "alias" ]]; then
+ sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/")
+ fi
+
if [ ! -f "$sf_cmd" ]; then
return 1
fi
diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json
index 2a7b429ac1f8b..9a565068cdedd 100644
--- a/src/Symfony/Component/Console/composer.json
+++ b/src/Symfony/Component/Console/composer.json
@@ -19,7 +19,7 @@
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php73": "^1.8",
+ "symfony/polyfill-php73": "^1.9",
"symfony/polyfill-php80": "^1.16",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/string": "^5.1|^6.0"
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php
index f5094996d949f..4db7185cf534b 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php
@@ -15,6 +15,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\LogicException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* @author Alexander M. Turek
@@ -99,11 +100,19 @@ protected function processValue($value, bool $isRoot = false)
}
}
- if ($this->parameterAttributeConfigurators && $constructorReflector = $this->getConstructor($value, false)) {
- foreach ($constructorReflector->getParameters() as $parameterReflector) {
- foreach ($parameterReflector->getAttributes() as $attribute) {
- if ($configurator = $this->parameterAttributeConfigurators[$attribute->getName()] ?? null) {
- $configurator($conditionals, $attribute->newInstance(), $parameterReflector);
+ if ($this->parameterAttributeConfigurators) {
+ try {
+ $constructorReflector = $this->getConstructor($value, false);
+ } catch (RuntimeException $e) {
+ $constructorReflector = null;
+ }
+
+ if ($constructorReflector) {
+ foreach ($constructorReflector->getParameters() as $parameterReflector) {
+ foreach ($parameterReflector->getAttributes() as $attribute) {
+ if ($configurator = $this->parameterAttributeConfigurators[$attribute->getName()] ?? null) {
+ $configurator($conditionals, $attribute->newInstance(), $parameterReflector);
+ }
}
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
index 1e5df4879673e..6624f74901320 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
@@ -976,6 +976,11 @@ static function (ChildDefinition $definition, CustomParameterAttribute $attribut
->setPublic(true)
->setAutoconfigured(true);
+ $container->register('failing_factory', \stdClass::class);
+ $container->register('ccc', TaggedService4::class)
+ ->setFactory([new Reference('failing_factory'), 'create'])
+ ->setAutoconfigured(true);
+
$collector = new TagCollector();
$container->addCompilerPass($collector);
@@ -996,6 +1001,17 @@ static function (ChildDefinition $definition, CustomParameterAttribute $attribut
['property' => 'name'],
['someAttribute' => 'on name', 'priority' => 0, 'property' => 'name'],
],
+ 'ccc' => [
+ ['class' => TaggedService4::class],
+ ['method' => 'fooAction'],
+ ['someAttribute' => 'on fooAction', 'priority' => 0, 'method' => 'fooAction'],
+ ['parameter' => 'param1'],
+ ['someAttribute' => 'on param1 in fooAction', 'priority' => 0, 'parameter' => 'param1'],
+ ['method' => 'barAction'],
+ ['someAttribute' => 'on barAction', 'priority' => 0, 'method' => 'barAction'],
+ ['property' => 'name'],
+ ['someAttribute' => 'on name', 'priority' => 0, 'property' => 'name'],
+ ],
], $collector->collectedTags);
}
diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
index 87e508405b734..ffb823eed23c2 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
@@ -615,7 +615,9 @@ public function handleException(\Throwable $exception)
}
$loggedErrors = $this->loggedErrors;
- $this->loggedErrors = $exception === $handlerException ? 0 : $this->loggedErrors;
+ if ($exception === $handlerException) {
+ $this->loggedErrors &= ~$type;
+ }
try {
$this->handleException($handlerException);
diff --git a/src/Symfony/Component/ErrorHandler/Tests/phpt/exception_rethrown.phpt b/src/Symfony/Component/ErrorHandler/Tests/phpt/exception_rethrown.phpt
index 06540f4530121..be5ce6a5cdffa 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/phpt/exception_rethrown.phpt
+++ b/src/Symfony/Component/ErrorHandler/Tests/phpt/exception_rethrown.phpt
@@ -16,7 +16,9 @@ if (true) {
{
public function log($level, $message, array $context = []): void
{
- echo 'LOG: ', $message, "\n";
+ if (0 !== strpos($message, 'Deprecated: ')) {
+ echo 'LOG: ', $message, "\n";
+ }
}
}
}
@@ -34,5 +36,5 @@ Exception {%S
#message: "foo"
#code: 0
#file: "%s"
- #line: 25
+ #line: 27
}
diff --git a/src/Symfony/Component/HttpClient/Response/MockResponse.php b/src/Symfony/Component/HttpClient/Response/MockResponse.php
index 71fe8fbd17592..e8f1226f8fe75 100644
--- a/src/Symfony/Component/HttpClient/Response/MockResponse.php
+++ b/src/Symfony/Component/HttpClient/Response/MockResponse.php
@@ -285,6 +285,10 @@ private static function readResponse(self $response, array $options, ResponseInt
'http_code' => $response->info['http_code'],
] + $info + $response->info;
+ if (null !== $response->info['error']) {
+ throw new TransportException($response->info['error']);
+ }
+
if (!isset($response->info['total_time'])) {
$response->info['total_time'] = microtime(true) - $response->info['start_time'];
}
diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
index 12bc2d88a6f76..9f47d10d02848 100644
--- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
@@ -18,7 +18,6 @@
use Symfony\Component\HttpClient\Response\ResponseStream;
use Symfony\Contracts\HttpClient\ChunkInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
-use Symfony\Contracts\HttpClient\ResponseInterface;
class MockHttpClientTest extends HttpClientTestCase
{
@@ -272,16 +271,8 @@ protected function getHttpClient(string $testCase): HttpClientInterface
break;
case 'testDnsError':
- $mock = $this->createMock(ResponseInterface::class);
- $mock->expects($this->any())
- ->method('getStatusCode')
- ->willThrowException(new TransportException('DSN error'));
- $mock->expects($this->any())
- ->method('getInfo')
- ->willReturn([]);
-
- $responses[] = $mock;
- $responses[] = $mock;
+ $responses[] = $mockResponse = new MockResponse('', ['error' => 'DNS error']);
+ $responses[] = $mockResponse;
break;
case 'testToStream':
@@ -296,12 +287,7 @@ protected function getHttpClient(string $testCase): HttpClientInterface
break;
case 'testTimeoutOnAccess':
- $mock = $this->createMock(ResponseInterface::class);
- $mock->expects($this->any())
- ->method('getHeaders')
- ->willThrowException(new TransportException('Timeout'));
-
- $responses[] = $mock;
+ $responses[] = new MockResponse('', ['error' => 'Timeout']);
break;
case 'testAcceptHeader':
@@ -363,16 +349,7 @@ protected function getHttpClient(string $testCase): HttpClientInterface
break;
case 'testMaxDuration':
- $mock = $this->createMock(ResponseInterface::class);
- $mock->expects($this->any())
- ->method('getContent')
- ->willReturnCallback(static function (): void {
- usleep(100000);
-
- throw new TransportException('Max duration was reached.');
- });
-
- $responses[] = $mock;
+ $responses[] = new MockResponse('', ['error' => 'Max duration was reached.']);
break;
}
diff --git a/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php b/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php
index def2c6f0ca9ac..f8c8d4cea5733 100644
--- a/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php
@@ -4,6 +4,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\Exception\JsonException;
+use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\Response\MockResponse;
/**
@@ -96,4 +97,14 @@ public function toArrayErrors()
'message' => 'JSON content was expected to decode to an array, "int" returned for "https://example.com/file.json".',
];
}
+
+ public function testErrorIsTakenIntoAccountInInitialization()
+ {
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('ccc error');
+
+ MockResponse::fromRequest('GET', 'https://symfony.com', [], new MockResponse('', [
+ 'error' => 'ccc error',
+ ]))->getStatusCode();
+ }
}
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index 10e91ca1be30d..d112b1f1835ea 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -1515,7 +1515,7 @@ public function isMethodCacheable()
public function getProtocolVersion()
{
if ($this->isFromTrustedProxy()) {
- preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
+ preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via') ?? '', $matches);
if ($matches) {
return 'HTTP/'.$matches[2];
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php
index 0f1ce5c2b8ecd..f3f7b201d9dea 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php
@@ -30,7 +30,7 @@ public static function createHandler($connection): AbstractSessionHandler
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a string or a connection object, "%s" given.', __METHOD__, get_debug_type($connection)));
}
- if ($options = parse_url($connection)) {
+ if ($options = \is_string($connection) ? parse_url($connection) : false) {
parse_str($options['query'] ?? '', $options);
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 227c1b51d6fe5..155863cf77b23 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -2256,7 +2256,10 @@ public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expec
$request = new Request();
$request->server->set('SERVER_PROTOCOL', $serverProtocol);
$request->server->set('REMOTE_ADDR', '1.1.1.1');
- $request->headers->set('Via', $via);
+
+ if (null !== $via) {
+ $request->headers->set('Via', $via);
+ }
$this->assertSame($expected, $request->getProtocolVersion());
}
@@ -2264,9 +2267,11 @@ public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expec
public function protocolVersionProvider()
{
return [
- 'untrusted without via' => ['HTTP/2.0', false, '', 'HTTP/2.0'],
+ 'untrusted with empty via' => ['HTTP/2.0', false, '', 'HTTP/2.0'],
+ 'untrusted without via' => ['HTTP/2.0', false, null, 'HTTP/2.0'],
'untrusted with via' => ['HTTP/2.0', false, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/2.0'],
- 'trusted without via' => ['HTTP/2.0', true, '', 'HTTP/2.0'],
+ 'trusted with empty via' => ['HTTP/2.0', true, '', 'HTTP/2.0'],
+ 'trusted without via' => ['HTTP/2.0', true, null, 'HTTP/2.0'],
'trusted with via' => ['HTTP/2.0', true, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'],
'trusted with via and protocol name' => ['HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'],
'trusted with broken via' => ['HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'],
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/SessionHandlerFactoryTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/SessionHandlerFactoryTest.php
index 46d6cd40151d5..9f06a7c8675da 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/SessionHandlerFactoryTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/SessionHandlerFactoryTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\SessionHandlerFactory;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
@@ -28,7 +29,7 @@ class SessionHandlerFactoryTest extends TestCase
/**
* @dataProvider provideConnectionDSN
*/
- public function testCreateHandler(string $connectionDSN, string $expectedPath, string $expectedHandlerType)
+ public function testCreateFileHandler(string $connectionDSN, string $expectedPath, string $expectedHandlerType)
{
$handler = SessionHandlerFactory::createHandler($connectionDSN);
@@ -45,4 +46,32 @@ public function provideConnectionDSN(): array
'native file handler using provided save_path' => ['connectionDSN' => 'file://'.$base.'/session/storage', 'expectedPath' => $base.'/session/storage', 'expectedHandlerType' => StrictSessionHandler::class],
];
}
+
+ /**
+ * @requires extension redis
+ */
+ public function testCreateRedisHandlerFromConnectionObject()
+ {
+ $handler = SessionHandlerFactory::createHandler($this->createMock(\Redis::class));
+ $this->assertInstanceOf(RedisSessionHandler::class, $handler);
+ }
+
+ /**
+ * @requires extension redis
+ */
+ public function testCreateRedisHandlerFromDsn()
+ {
+ $handler = SessionHandlerFactory::createHandler('redis://localhost?prefix=foo&ttl=3600&ignored=bar');
+ $this->assertInstanceOf(RedisSessionHandler::class, $handler);
+
+ $reflection = new \ReflectionObject($handler);
+
+ $prefixProperty = $reflection->getProperty('prefix');
+ $prefixProperty->setAccessible(true);
+ $this->assertSame('foo', $prefixProperty->getValue($handler));
+
+ $ttlProperty = $reflection->getProperty('ttl');
+ $ttlProperty->setAccessible(true);
+ $this->assertSame('3600', $ttlProperty->getValue($handler));
+ }
}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
index 0867cad073dea..08b6faac0e7e9 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
@@ -146,7 +146,7 @@ public function onKernelResponse(ResponseEvent $event)
$sessionCookieHttpOnly = $this->sessionOptions['cookie_httponly'] ?? true;
$sessionCookieSameSite = $this->sessionOptions['cookie_samesite'] ?? Cookie::SAMESITE_LAX;
- SessionUtils::popSessionCookie($sessionName, $sessionCookiePath);
+ SessionUtils::popSessionCookie($sessionName, $sessionId);
$request = $event->getRequest();
$requestSessionCookieId = $request->cookies->get($sessionName);
diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php
index 5d77377c6046b..f19e13649e988 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php
@@ -70,6 +70,7 @@ private function setLocale(Request $request)
$request->setLocale($locale);
} elseif ($this->useAcceptLanguageHeader && $this->enabledLocales && ($preferredLanguage = $request->getPreferredLanguage($this->enabledLocales))) {
$request->setLocale($preferredLanguage);
+ $request->attributes->set('_vary_by_language', true);
}
}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php b/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php
index bb51c6dc0dbda..a4090159bb751 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php
@@ -50,6 +50,9 @@ public function onKernelResponse(ResponseEvent $event)
if ($this->addContentLanguageHeader && !$response->isInformational() && !$response->isEmpty() && !$response->headers->has('Content-Language')) {
$response->headers->set('Content-Language', $event->getRequest()->getLocale());
+ }
+
+ if ($event->getRequest()->attributes->get('_vary_by_language')) {
$response->setVary('Accept-Language', false);
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 656324a4df7e8..35353377029cc 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static $freshCache = [];
- public const VERSION = '5.4.0';
- public const VERSION_ID = 50400;
+ public const VERSION = '5.4.1';
+ public const VERSION_ID = 50401;
public const MAJOR_VERSION = 5;
public const MINOR_VERSION = 4;
- public const RELEASE_VERSION = 0;
+ public const RELEASE_VERSION = 1;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2024';
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
index d82aba64513e4..9924c27d11af9 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
@@ -312,6 +312,7 @@ public function testSessionUsageLogIfStatelessAndSessionUsed()
public function testSessionIsSavedWhenUnexpectedSessionExceptionThrown()
{
$session = $this->createMock(Session::class);
+ $session->expects($this->exactly(1))->method('getId')->willReturn('123456');
$session->expects($this->exactly(1))->method('getName')->willReturn('PHPSESSID');
$session->method('isStarted')->willReturn(true);
$session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1));
diff --git a/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php b/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php
index ead96843fbb78..1a94f3fc64711 100644
--- a/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php
+++ b/src/Symfony/Component/Lock/Store/DoctrineDbalStore.php
@@ -14,6 +14,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception as DBALException;
+use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\Lock\Exception\InvalidArgumentException;
@@ -90,6 +91,9 @@ public function save(Key $key)
ParameterType::STRING,
ParameterType::STRING,
]);
+ } catch (TableNotFoundException $e) {
+ $this->createTable();
+ $this->save($key);
} catch (DBALException $e) {
// the lock is already acquired. It could be us. Let's try to put off.
$this->putOffExpiration($key, $this->initialTtl);
diff --git a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php
index c20e2d3088111..6a89e49399b0c 100644
--- a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php
+++ b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php
@@ -70,7 +70,6 @@ public function testDsn(string $dsn, string $file = null)
try {
$store = new DoctrineDbalStore($dsn);
- $store->createTable();
$store->save($key);
$this->assertTrue($store->exists($key));
diff --git a/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php
index 69968a2f11b80..dd15f0f1614b9 100644
--- a/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php
+++ b/src/Symfony/Component/Lock/Tests/Store/PdoStoreTest.php
@@ -84,7 +84,6 @@ public function testDsn(string $dsn, string $file = null)
try {
$store = new PdoStore($dsn);
- $store->createTable();
$store->save($key);
$this->assertTrue($store->exists($key));
diff --git a/src/Symfony/Component/Lock/composer.json b/src/Symfony/Component/Lock/composer.json
index dbb01a0056ee4..c2b8e3078e756 100644
--- a/src/Symfony/Component/Lock/composer.json
+++ b/src/Symfony/Component/Lock/composer.json
@@ -23,7 +23,6 @@
},
"require-dev": {
"doctrine/dbal": "^2.13|^3.0",
- "mongodb/mongodb": "~1.1",
"predis/predis": "~1.0"
},
"conflict": {
diff --git a/src/Symfony/Component/Runtime/composer.json b/src/Symfony/Component/Runtime/composer.json
index f775ec3a0e94d..f8112d9a43d00 100644
--- a/src/Symfony/Component/Runtime/composer.json
+++ b/src/Symfony/Component/Runtime/composer.json
@@ -21,10 +21,10 @@
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
- "symfony/console": "^5.4|^6.0",
+ "symfony/console": "^4.4.30|^5.3.7|^6.0",
"symfony/dotenv": "^5.1|^6.0",
- "symfony/http-foundation": "^4.4|^5.0|^6.0",
- "symfony/http-kernel": "^4.4|^5.0|^6.0"
+ "symfony/http-foundation": "^4.4.30|^5.3.7|^6.0",
+ "symfony/http-kernel": "^4.4.30|^5.3.7|^6.0"
},
"conflict": {
"symfony/dotenv": "<5.1"
diff --git a/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php b/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php
index f40e2119f644d..77978f93963e0 100644
--- a/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php
+++ b/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php
@@ -90,47 +90,37 @@ public function read(array $domains, array $locales): TranslatorBag
{
$domains = $domains ?: ['*'];
$translatorBag = new TranslatorBag();
- $responses = [];
foreach ($locales as $locale) {
foreach ($domains as $domain) {
- $responses[] = [
- 'response' => $this->client->request('GET', sprintf('export/locale/%s.xlf', rawurlencode($locale)), [
- 'query' => [
- 'filter' => $domain,
- 'status' => 'translated',
- ],
- ]),
- 'locale' => $locale,
- 'domain' => $domain,
- ];
- }
- }
-
- foreach ($responses as $response) {
- $locale = $response['locale'];
- $domain = $response['domain'];
- $response = $response['response'];
+ // Loco forbids concurrent requests, so the requests must be synchronous in order to prevent "429 Too Many Requests" errors.
+ $response = $this->client->request('GET', sprintf('export/locale/%s.xlf', rawurlencode($locale)), [
+ 'query' => [
+ 'filter' => $domain,
+ 'status' => 'translated',
+ ],
+ ]);
+
+ if (404 === $response->getStatusCode()) {
+ $this->logger->warning(sprintf('Locale "%s" for domain "%s" does not exist in Loco.', $locale, $domain));
+ continue;
+ }
- if (404 === $response->getStatusCode()) {
- $this->logger->warning(sprintf('Locale "%s" for domain "%s" does not exist in Loco.', $locale, $domain));
- continue;
- }
+ $responseContent = $response->getContent(false);
- $responseContent = $response->getContent(false);
+ if (200 !== $response->getStatusCode()) {
+ throw new ProviderException('Unable to read the Loco response: '.$responseContent, $response);
+ }
- if (200 !== $response->getStatusCode()) {
- throw new ProviderException('Unable to read the Loco response: '.$responseContent, $response);
- }
+ $locoCatalogue = $this->loader->load($responseContent, $locale, $domain);
+ $catalogue = new MessageCatalogue($locale);
- $locoCatalogue = $this->loader->load($responseContent, $locale, $domain);
- $catalogue = new MessageCatalogue($locale);
+ foreach ($locoCatalogue->all($domain) as $key => $message) {
+ $catalogue->set($this->retrieveKeyFromId($key, $domain), $message, $domain);
+ }
- foreach ($locoCatalogue->all($domain) as $key => $message) {
- $catalogue->set($this->retrieveKeyFromId($key, $domain), $message, $domain);
+ $translatorBag->addCatalogue($catalogue);
}
-
- $translatorBag->addCatalogue($catalogue);
}
return $translatorBag;
diff --git a/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php b/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php
index 8177c2c0c873e..e5726f266c77d 100644
--- a/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php
+++ b/src/Symfony/Component/Translation/Tests/Command/TranslationPullCommandTest.php
@@ -478,6 +478,83 @@ public function testPullMessagesWithDefaultLocale()
, file_get_contents($filenameFr));
}
+ public function testPullMessagesMultipleDomains()
+ {
+ $arrayLoader = new ArrayLoader();
+ $filenameMessages = $this->createFile(['note' => 'NOTE']);
+ $filenameDomain = $this->createFile(['note' => 'NOTE'], 'en', 'domain.%locale%.xlf');
+ $locales = ['en'];
+ $domains = ['messages', 'domain'];
+
+ $providerReadTranslatorBag = new TranslatorBag();
+ $providerReadTranslatorBag->addCatalogue($arrayLoader->load([
+ 'new.foo' => 'newFoo',
+ ], 'en'));
+
+ $providerReadTranslatorBag->addCatalogue($arrayLoader->load([
+ 'new.foo' => 'newFoo',
+ ], 'en',
+ 'domain'
+ ));
+
+ $provider = $this->createMock(ProviderInterface::class);
+ $provider->expects($this->once())
+ ->method('read')
+ ->with($domains, $locales)
+ ->willReturn($providerReadTranslatorBag);
+
+ $provider->expects($this->once())
+ ->method('__toString')
+ ->willReturn('null://default');
+
+ $tester = $this->createCommandTester($provider, $locales, $domains, 'en');
+ $tester->execute(['--locales' => ['en'], '--domains' => ['messages', 'domain']]);
+
+ $this->assertStringContainsString('[OK] New translations from "null" has been written locally (for "en" locale(s), and "messages, domain" domain(s)).', trim($tester->getDisplay()));
+ $this->assertXmlStringEqualsXmlString(<<
+
+
+
+
+
+ new.foo
+ newFoo
+
+
+ note
+ NOTE
+
+
+
+
+XLIFF
+ , file_get_contents($filenameMessages));
+ $this->assertXmlStringEqualsXmlString(<<
+
+
+
+
+
+ new.foo
+ newFoo
+
+
+ note
+ NOTE
+
+
+
+
+XLIFF
+ , file_get_contents($filenameDomain));
+ }
+
/**
* @dataProvider provideCompletionSuggestions
*/
diff --git a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php
index 6ca3eab41fd6e..aeaef472fb03d 100644
--- a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php
@@ -15,13 +15,15 @@
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
-final class ConstraintValidatorTest extends TestCase
+class ConstraintValidatorTest extends TestCase
{
/**
* @dataProvider formatValueProvider
*/
public function testFormatValue($expected, $value, $format = 0)
{
+ \Locale::setDefault('en');
+
$this->assertSame($expected, (new TestFormatValueConstraintValidator())->formatValueProxy($value, $format));
}
diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json
index 1639a1603a1aa..40d942a1dca61 100644
--- a/src/Symfony/Component/Validator/composer.json
+++ b/src/Symfony/Component/Validator/composer.json
@@ -47,7 +47,7 @@
"conflict": {
"doctrine/annotations": "<1.13",
"doctrine/cache": "<1.11",
- "doctrine/lexer": "<1.0.2",
+ "doctrine/lexer": "<1.1",
"phpunit/phpunit": "<5.4.3",
"symfony/dependency-injection": "<4.4",
"symfony/expression-language": "<5.1",
diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
index a57e8b9b6995a..274ee0d98f7da 100644
--- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
@@ -144,7 +144,7 @@ public static function castReflectionGenerator(\ReflectionGenerator $c, array $a
array_unshift($trace, [
'function' => 'yield',
'file' => $function->getExecutingFile(),
- 'line' => $function->getExecutingLine() - 1,
+ 'line' => $function->getExecutingLine() - (int) (\PHP_VERSION_ID < 80100),
]);
$trace[] = $frame;
$a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1);
@@ -289,15 +289,17 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st
unset($a[$prefix.'allowsNull']);
}
- try {
- $a[$prefix.'default'] = $v = $c->getDefaultValue();
- if ($c->isDefaultValueConstant()) {
- $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
- }
- if (null === $v) {
- unset($a[$prefix.'allowsNull']);
+ if ($c->isOptional()) {
+ try {
+ $a[$prefix.'default'] = $v = $c->getDefaultValue();
+ if ($c->isDefaultValueConstant()) {
+ $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
+ }
+ if (null === $v) {
+ unset($a[$prefix.'allowsNull']);
+ }
+ } catch (\ReflectionException $e) {
}
- } catch (\ReflectionException $e) {
}
return $a;
diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php
index 901df856377e6..c261a0da8c168 100644
--- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php
+++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php
@@ -138,21 +138,8 @@ public function testReflectionParameter()
{
$var = new \ReflectionParameter(reflectionParameterFixture::class, 0);
- if (\PHP_VERSION_ID < 80100) {
- $this->assertDumpMatchesFormat(
- <<<'EOTXT'
-ReflectionParameter {
- +name: "arg1"
- position: 0
- typeHint: "Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass"
- default: null
-}
-EOTXT
- , $var
- );
- } else {
- $this->assertDumpMatchesFormat(
- <<<'EOTXT'
+ $this->assertDumpMatchesFormat(
+ <<<'EOTXT'
ReflectionParameter {
+name: "arg1"
position: 0
@@ -161,8 +148,7 @@ public function testReflectionParameter()
}
EOTXT
, $var
- );
- }
+ );
}
public function testReflectionParameterScalar()
@@ -484,38 +470,20 @@ public function testGenerator()
$generator = new GeneratorDemo();
$generator = $generator->baz();
- if (\PHP_VERSION_ID < 80100) {
- $expectedDump = <<<'EODUMP'
+ $expectedDump = <<<'EODUMP'
Generator {
this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
- executing: {
+ %s: {
%sGeneratorDemo.php:14 {
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz()
› {
› yield from bar();
› }
}
- }
+%A}
closed: false
}
EODUMP;
- } else {
- $expectedDump = <<<'EODUMP'
-Generator {
- this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
- trace: {
- ./src/Symfony/Component/VarDumper/Tests/Fixtures/GeneratorDemo.php:13 {
- Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz()
- › public function baz()
- › {
- › yield from bar();
- }
- Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() {}
- }
- closed: false
-}
-EODUMP;
- }
$this->assertDumpMatchesFormat($expectedDump, $generator);
@@ -523,69 +491,33 @@ public function testGenerator()
break;
}
- if (\PHP_VERSION_ID < 80100) {
- $expectedDump = <<<'EODUMP'
+ $expectedDump = <<<'EODUMP'
array:2 [
0 => ReflectionGenerator {
this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
- trace: {
- %s%eTests%eFixtures%eGeneratorDemo.php:9 {
+ %s: {
+ %s%eTests%eFixtures%eGeneratorDemo.php:%d {
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo()
- › {
- › yield 1;
- › }
- }
+%A › yield 1;
+%A }
%s%eTests%eFixtures%eGeneratorDemo.php:20 { …}
%s%eTests%eFixtures%eGeneratorDemo.php:14 { …}
- }
+%A }
closed: false
}
1 => Generator {
- executing: {
- %sGeneratorDemo.php:10 {
+ %s: {
+ %s%eTests%eFixtures%eGeneratorDemo.php:%d {
Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo()
› yield 1;
› }
›
}
- }
- closed: false
- }
-]
-EODUMP;
- } else {
- $expectedDump = <<<'EODUMP'
-array:2 [
- 0 => ReflectionGenerator {
- this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
- trace: {
- %s%eTests%eFixtures%eGeneratorDemo.php:9 {
- Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo()
- › {
- › yield 1;
- › }
- }
- %s%eTests%eFixtures%eGeneratorDemo.php:20 { …}
- %s%eTests%eFixtures%eGeneratorDemo.php:14 { …}
- Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() {}
- }
- closed: false
- }
- 1 => Generator {
- trace: {
- ./src/Symfony/Component/VarDumper/Tests/Fixtures/GeneratorDemo.php:9 {
- Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo()
- › {
- › yield 1;
- › }
- }
- Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() {}
- }
+%A }
closed: false
}
]
EODUMP;
- }
$r = new \ReflectionGenerator($generator);
$this->assertDumpMatchesFormat($expectedDump, [$r, $r->getExecutingGenerator()]);
|