diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.md b/.github/ISSUE_TEMPLATE/1_Bug_report.md
deleted file mode 100644
index aef16611e0f7..000000000000
--- 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 5518d4e4ad79..ef0f72c79427 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 908c5ee52664..000000000000
--- 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 9480710c1565..000000000000
--- 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 0855c3c5f1e1..000000000000
--- 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 000000000000..34227566ed84
--- /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 62662f876fd3..5e7092d38591 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 9135a1da666e..a3682af0b9d1 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 cb66e2d8d3b0..3c0a7c36be89 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 c60d92ba61cc..c10c70de1abb 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 5ba6dab3b612..5cc4204a52f3 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 a2ce35807705..d7492d23f3cf 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 10c9dcc9199e..347263738c7b 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 7a2ad9a8d9cd..cd88bedca1e3 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 a004935a6afd..dd7dabcc87db 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 ab35078f85fc..c1c698aea0eb 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 c1caadd9df3d..a151c8703dd3 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 f9772266722e..f2e0d9520c4b 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 137311bd6358..2df5b72559c6 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 9b761dbdd393..3c46bc460998 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 7ae8f336b740..8c32625b1002 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 00b8d8aafbd5..53613d3b5020 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 ce653c6bfaaa..a506ac2d2915 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 6fe970f366f3..d64b2c38ac7e 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 0771f15a803b..91e75ce0c534 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 8747273b9e9a..4f8c8ce127a1 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 e393772ae4b2..265e1d1c83d3 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 3f257dea1fa5..87d3e65394ed 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 971af088ba11..c5e89c3c282a 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 2a7b429ac1f8..9a565068cded 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 f5094996d949..4db7185cf534 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 1e5df4879673..6624f7490132 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 87e508405b73..ffb823eed23c 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 06540f453012..be5ce6a5cdff 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 71fe8fbd1759..e8f1226f8fe7 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 12bc2d88a6f7..9f47d10d0284 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 def2c6f0ca9a..f8c8d4cea573 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 10e91ca1be30..d112b1f1835e 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 0f1ce5c2b8ec..f3f7b201d9de 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 227c1b51d6fe..155863cf77b2 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 46d6cd40151d..9f06a7c8675d 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 0867cad073de..08b6faac0e7e 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 5d77377c6046..f19e13649e98 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 bb51c6dc0dbd..a4090159bb75 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 656324a4df7e..35353377029c 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 d82aba64513e..9924c27d11af 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 ead96843fbb7..1a94f3fc6471 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 c20e2d308811..6a89e49399b0 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 69968a2f11b8..dd15f0f1614b 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 dbb01a0056ee..c2b8e3078e75 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 f775ec3a0e94..f8112d9a43d0 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 f40e2119f644..77978f93963e 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 8177c2c0c873..e5726f266c77 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 6ca3eab41fd6..aeaef472fb03 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 1639a1603a1a..40d942a1dca6 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 a57e8b9b6995..274ee0d98f7d 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 901df856377e..c261a0da8c16 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()]);
|