diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5e7092d385910..5b76de89c7bee 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,7 +15,7 @@ Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - Bug fixes must be submitted against the lowest maintained branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too.) - - Features and deprecations must be submitted against branch 5.x. + - Features and deprecations must be submitted against the latest branch. - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff index 038db511c4403..71389a22b9149 100644 --- a/.github/expected-missing-return-types.diff +++ b/.github/expected-missing-return-types.diff @@ -6,10 +6,10 @@ head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff) (echo "$head" && echo && git diff -U2 composer.json src/) > .github/expected-missing-return-types.diff diff --git a/composer.json b/composer.json -index 1bc576b112..589ef8c260 100644 +index 978743d34d..1f185f682c 100644 --- a/composer.json +++ b/composer.json -@@ -175,5 +175,5 @@ +@@ -180,5 +180,5 @@ ], "exclude-from-classmap": [ - "**/Tests/" @@ -144,7 +144,7 @@ index 6b1c6c5fbe..bb80ed461e 100644 + public function isFresh(ResourceInterface $resource, int $timestamp): bool; } diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php -index b582435f7d..3d5f064773 100644 +index 09234f5eb5..24d995ad6d 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -218,5 +218,5 @@ class Application implements ResetInterface @@ -197,7 +197,7 @@ index b582435f7d..3d5f064773 100644 { foreach ($command->getHelperSet() as $helper) { diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php -index d6354b4ab1..b267917312 100644 +index e69bae0982..3390628d0d 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -171,5 +171,5 @@ class Command @@ -250,10 +250,10 @@ index 024da1884e..943790e875 100644 /** diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php -index 1acec50de5..904e67a47b 100644 +index e9fa5a6808..016e9d893a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php -@@ -70,5 +70,5 @@ abstract class AbstractRecursivePass implements CompilerPassInterface +@@ -71,5 +71,5 @@ abstract class AbstractRecursivePass implements CompilerPassInterface * @return mixed */ - protected function processValue(mixed $value, bool $isRoot = false) @@ -372,7 +372,7 @@ index 479aeef880..272954c082 100644 + public function getFunctions(): array; } diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php -index 79d61e8bc0..7f34d95d84 100644 +index 5a077a42a6..62a234b116 100644 --- a/src/Symfony/Component/Form/AbstractExtension.php +++ b/src/Symfony/Component/Form/AbstractExtension.php @@ -114,5 +114,5 @@ abstract class AbstractExtension implements FormExtensionInterface @@ -514,10 +514,10 @@ index 2f442cb536..d98909cfae 100644 + public function warmUp(string $cacheDir): array; } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php -index 3a3be3af49..971560c07b 100644 +index 1289cf0ea9..dabeec97ee 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php -@@ -59,5 +59,5 @@ abstract class DataCollector implements DataCollectorInterface +@@ -58,5 +58,5 @@ abstract class DataCollector implements DataCollectorInterface * @return callable[] The casters to add to the cloner */ - protected function getCasters() @@ -535,7 +535,7 @@ index 1cb865fd66..f6f4efe7a7 100644 + public function getName(): string; } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php -index 8e7b80a909..d757429d36 100644 +index eeec55593f..c12df944fc 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -448,5 +448,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface @@ -546,7 +546,7 @@ index 8e7b80a909..d757429d36 100644 { if ($this->surrogate) { diff --git a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php -index f4773eb62a..0ba648b57f 100644 +index fc70d8970d..9dde6d1f93 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php +++ b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php @@ -61,5 +61,5 @@ class HttpKernelBrowser extends AbstractBrowser @@ -665,6 +665,17 @@ index fbb37d9f94..522e0487a9 100644 - public function isIndex(int $index); + public function isIndex(int $index): bool; } +diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +index cfffdb2f71..3e2261898a 100644 +--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php ++++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +@@ -725,5 +725,5 @@ class PropertyAccessorTest extends TestCase + * @return mixed + */ +- public function getFoo() ++ public function getFoo(): mixed + { + return $this->foo; diff --git a/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php index f9ee787130..61f8b6d5be 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php @@ -714,7 +725,7 @@ index 2dd0e5efbf..95e01d8955 100644 { $name = str_replace('\\', '_', $class->name).'_'.$method->name; diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php -index 83c10427a1..e113d4a194 100644 +index be653e4f00..e46300d474 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -178,5 +178,5 @@ class Router implements RouterInterface, RequestMatcherInterface @@ -756,7 +767,7 @@ index 7e401c3ff3..6b446ff376 100644 + public function vote(TokenInterface $token, mixed $subject, array $attributes): int; } diff --git a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php -index 298bc78cd9..3ef4176dd8 100644 +index 9e2b02b603..c6a753f1f7 100644 --- a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php +++ b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php @@ -89,5 +89,5 @@ class AuthenticationException extends RuntimeException @@ -795,7 +806,7 @@ index 91271d14a3..100c2fb549 100644 + public function start(Request $request, AuthenticationException $authException = null): Response; } diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php -index 0c313f8f09..acfc9f4b88 100644 +index 546b77d22f..24038d4b94 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -106,5 +106,5 @@ class Firewall implements EventSubscriberInterface @@ -833,7 +844,7 @@ index 84a84ad1f3..6f66b6d32a 100644 + public function supportsDecoding(string $format): bool; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php -index b0a1f3218b..893fe59e2f 100644 +index 7f86ed8d78..cf084423ec 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -223,5 +223,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn @@ -971,31 +982,31 @@ index 4c088b94f9..86107a636d 100644 + abstract protected function extractFromDirectory(string|array $resource): iterable; } diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php -index d6dcdf178f..0ab8d9c10e 100644 +index 46432f2f4c..47ac39d5e3 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php -@@ -239,5 +239,5 @@ abstract class Constraint +@@ -243,5 +243,5 @@ abstract class Constraint * @see __construct() */ - public function getDefaultOption() + public function getDefaultOption(): ?string { return null; -@@ -253,5 +253,5 @@ abstract class Constraint +@@ -257,5 +257,5 @@ abstract class Constraint * @see __construct() */ - public function getRequiredOptions() + public function getRequiredOptions(): array { return []; -@@ -267,5 +267,5 @@ abstract class Constraint +@@ -271,5 +271,5 @@ abstract class Constraint * @return string */ - public function validatedBy() + public function validatedBy(): string { return static::class.'Validator'; -@@ -281,5 +281,5 @@ abstract class Constraint +@@ -285,5 +285,5 @@ abstract class Constraint * @return string|string[] One or more constant values */ - public function getTargets() diff --git a/.github/patch-types.php b/.github/patch-types.php index dbbb375ab106b..86b1c95c72ec5 100644 --- a/.github/patch-types.php +++ b/.github/patch-types.php @@ -31,6 +31,7 @@ case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Preload/'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/BadClasses/MissingParent.php'): case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/'): + case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberIntersectionWithTrait.php'): case false !== strpos($file, '/src/Symfony/Component/ErrorHandler/Tests/Fixtures/'): case false !== strpos($file, '/src/Symfony/Component/Form/Tests/Fixtures/Answer.php'): case false !== strpos($file, '/src/Symfony/Component/Form/Tests/Fixtures/Number.php'): diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 89f7bf8929467..56f3c44954d63 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -122,7 +122,7 @@ jobs: uses: shivammathur/setup-php@v2 with: coverage: "none" - extensions: "json,couchbase,memcached,mongodb-1.10.0,redis,rdkafka,xsl,ldap" + extensions: "json,couchbase,memcached,mongodb-1.10.0,redis-5.3.4,rdkafka,xsl,ldap" ini-values: date.timezone=Europe/Paris,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 php-version: "${{ matrix.php }}" tools: pecl diff --git a/.github/workflows/package-tests.yml b/.github/workflows/package-tests.yml index 3c0a7c36be89f..23b65286814a9 100644 --- a/.github/workflows/package-tests.yml +++ b/.github/workflows/package-tests.yml @@ -82,7 +82,7 @@ jobs: echo "Verifying new package" _correct_license_file $DIR/LICENSE || localExit=1 - if [ $TYPE == 'component_bridge' ]; 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 3b4922f87fbde..7c1e9139b9b9d 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -212,6 +212,11 @@ jobs: [[ ! $X ]] || (exit 1) + - name: Run TTY tests + if: "! matrix.mode" + run: | + script -e -c './phpunit --group tty' /dev/null + - name: Run tests with SIGCHLD enabled PHP if: "matrix.php == '8.0' && ! matrix.mode" run: | diff --git a/CHANGELOG-6.0.md b/CHANGELOG-6.0.md index 964b98e9923c4..743f2e2e4dca5 100644 --- a/CHANGELOG-6.0.md +++ b/CHANGELOG-6.0.md @@ -7,6 +7,52 @@ in 6.0 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/v6.0.0...v6.0.1 +* 6.0.3 (2022-01-28) + + * bug #45193 [FrameworkBundle] Fix missing arguments when a serialization default context is bound (ArnoudThibaut) + * bug #44997 [Runtime] Fix --env and --no-debug with dotenv_overload (fancyweb) + * bug #45188 [Dotenv] Fix bootEnv() override with .env.local.php when the env key already exists (fancyweb) + * bug #45095 [Finder] Fix finding VCS re-included files in excluded directory (julienfalque) + * bug #44987 [DoctrineBridge] Fix automapping (mbabker) + * bug #44860 [Validator] Fix Choice constraint with associative choices array (derrabus) + * bug #44939 [Form] UrlType should not add protocol to emails (GromNaN) + * bug #43149 Silence warnings during tty detection (neclimdul) + * bug #45154 [Serializer] Fix AbstractObjectNormalizer not considering pseudo type false (Thomas Nunninger) + * bug #45185 [Notifier] Fix encoding of messages with FreeMobileTransport (94noni) + * bug #45181 [Console] Fix PHP 8.1 deprecation in ChoiceQuestion (BrokenSourceCode) + * bug #44634 [HttpKernel] Fix compatibility with php bridge and already started php sessions (alexander-schranz) + * bug #45174 [Notifier] Use the UTF-8 encoding in smsapi-notifier (marphi) + * bug #45140 [Yaml] Making the parser stateless (mamazu) + * bug #45109 [Console] fix restoring stty mode on CTRL+C (nicolas-grekas) + * bug #45103 [Process] Avoid calling fclose on an already closed resource (Seldaek) + * bug #44941 [RateLimiter] Resolve crash on near-round timestamps (xesxen) + * bug #45088 [Console] fix parsing escaped chars in StringInput (nicolas-grekas) + * bug #45096 [Cache] Throw exception if incompatible version of psr/simple-cache is used (colinodell) + * bug #45067 [RateLimiter] Implicit conversion fix (brian978) + * bug #45063 [DependencyInjection] remove arbitratry limitation to exclude inline services from bindings (nicolas-grekas) + * bug #44986 [DependencyInjection] copy synthetic status when resolving child definitions (kbond) + * bug #45073 [HttpClient] Fix Failed to open stream: Too many open files (adrienfr) + * bug #45053 [Console] use STDOUT/ERR in ConsoleOutput to save opening too many file descriptors (nicolas-grekas) + * bug #45029 [Cache] Set mtime of cache files 1 year into future if they do not expire (Blacksmoke16) + * bug #45012 [DoctrineBridge] Fix invalid guess with enumType (jderusse) + * bug #45015 [HttpClient] fix resetting DNS/etc when calling CurlHttpClient::reset() (nicolas-grekas) + * bug #45004 [HttpClient] Remove deprecated usage of GuzzleHttp\Promise\promise_for (plozmun) + * bug #44998 [FrameworkBundle] Allow default cache pools to be overwritten by user (Seldaek) + * bug #44890 [HttpClient] Remove deprecated usage of `GuzzleHttp\Promise\queue` (GrahamCampbell) + * bug #45002 [PropertyAccess] Fix handling of uninitialized property of anonymous class (filiplikavcan) + * bug #44979 [DependencyInjection] Add iterable to possible binding type (vladimir.panivko) + * bug #44908 [Serializer] Fix AbstractObjectNormalizer TypeError on denormalization (JustDylan23) + * bug #44976 [FrameworkBundle] Avoid calling rtrim(null, '/') in AssetsInstallCommand (pavol-tk, GromNaN) + * bug #44879 [DependencyInjection] Ignore argument type check in CheckTypeDeclarationsPass if it's a Definition with a factory (fancyweb) + * bug #44920 Use correct tag for ExpoTransportFactory service (jschaedl) + * bug #44931 Allow a zero time-limit for messenger:consume (fritzmg) + * bug #44932 [DependencyInjection] Fix nested env var with resolve processor (Laurent Moreau) + * bug #44912 [Console] Allow OutputFormatter::escape() to be used for escaping URLs used in (Seldaek) + * bug #44877 [Validator] Error using CssColor with doctrine annotations (sormes) + * bug #44878 [HttpClient] Turn negative timeout to a very long timeout (fancyweb) + * bug #44854 [Validator] throw when Constraint::_construct() has not been called (nicolas-grekas) + * bug #44857 [Translation] [LocoProvider] Fix use of asset ids (danut007ro) + * 6.0.2 (2021-12-29) * bug #44828 [Lock] Release DoctrineDbalPostgreSqlStore connection lock on failure (simon-watiau) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d211dd419d064..c0b3daf15d463 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,83 +1,8 @@ -Code of Conduct -=============== +# Code of Conduct -Our Pledge ----------- +This project follows a [Code of Conduct][code_of_conduct] in order to ensure an open and welcoming environment. +Please read the full text for understanding the accepted and unaccepted behavior. +Please read also the [reporting guidelines][guidelines], in case you encountered or witnessed any misbehavior. -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnic origin, gender identity and expression, level of -experience, education, socio-economic status, nationality, personal appearance, -religion, or sexual identity and orientation. - -Our Standards -------------- - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -Our Responsibilities --------------------- - -[CoC Active Response Ensurers, or CARE][1], are responsible for clarifying the -standards of acceptable behavior and are expected to take appropriate and fair -corrective action in response to any instances of unacceptable behavior. - -CARE team members have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, or to ban temporarily or permanently any -contributor for other behaviors that they deem inappropriate, threatening, -offensive, or harmful. - -Scope ------ - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by CARE team members. - -Enforcement ------------ - -Instances of abusive, harassing, or otherwise unacceptable behavior -[may be reported][2] by contacting the [CARE team members][1]. -All complaints will be reviewed and investigated and will result in a response -that is deemed necessary and appropriate to the circumstances. The CARE team is -obligated to maintain confidentiality with regard to the reporter of an -incident. Further details of specific enforcement policies may be posted -separately. - -CARE team members who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by the -[core team][3]. - -Attribution ------------ - -This Code of Conduct is adapted from the [Contributor Covenant version 1.4][4]. - -[1]: https://symfony.com/doc/current/contributing/code_of_conduct/care_team.html -[2]: https://symfony.com/doc/current/contributing/code_of_conduct/reporting_guidelines.html -[3]: https://symfony.com/doc/current/contributing/code/core_team.html -[4]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +[code_of_conduct]: https://symfony.com/coc +[guidelines]: https://symfony.com/doc/current/contributing/code_of_conduct/reporting_guidelines.html diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b5e87ad1280f4..48ae19030db84 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -12,15 +12,15 @@ The Symfony Connect username in parenthesis allows to get more information - Tobias Schultze (tobion) - Robin Chalas (chalas_r) - Christophe Coevoet (stof) - - Jérémy DERUSSÉ (jderusse) - Wouter De Jong (wouterj) + - Jérémy DERUSSÉ (jderusse) - Grégoire Pineau (lyrixx) - Maxime Steinhausser (ogizanagi) - Kévin Dunglas (dunglas) - Jordi Boggiano (seldaek) + - Thomas Calvet (fancyweb) - Victor Berchet (victor) - Javier Eguiluz (javier.eguiluz) - - Thomas Calvet (fancyweb) - Ryan Weaver (weaverryan) - Roland Franssen (ro0) - Jakub Zalas (jakubzalas) @@ -54,19 +54,19 @@ The Symfony Connect username in parenthesis allows to get more information - Valentin Udaltsov (vudaltsov) - Iltar van der Berg (kjarli) - Jonathan Wage (jwage) + - Jérôme Tamarelle (gromnan) - Matthias Pigulla (mpdude) - Diego Saint Esteben (dosten) - Grégoire Paris (greg0ire) - Alexandre Salomé (alexandresalome) - - Jérôme Tamarelle (gromnan) - William Durand (couac) - ornicar + - Titouan Galopin (tgalopin) - Konstantin Myakshin (koc) - Dany Maillard (maidmaid) - Francis Besset (francisbesset) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - - Titouan Galopin (tgalopin) - Laurent VOULLEMIER (lvo) - Vasilij Dusko | CREATION - Bulat Shakirzyanov (avalanche123) @@ -79,10 +79,10 @@ The Symfony Connect username in parenthesis allows to get more information - Miha Vrhovnik - Diego Saint Esteben (dii3g0) - Mathieu Piot (mpiot) + - Antoine M (amakdessi) - Konstantin Kudryashov (everzet) - Vladimir Reznichenko (kalessil) - Bilal Amarni (bamarni) - - Antoine M (amakdessi) - Florin Patan (florinpatan) - Jáchym Toušek (enumag) - Alex Pott @@ -107,24 +107,24 @@ The Symfony Connect username in parenthesis allows to get more information - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) + - Mathieu Santostefano (welcomattic) - Daniel Holmes (dholmes) - Sebastiaan Stok (sstok) + - HypeMC (hypemc) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - - Mathieu Santostefano (welcomattic) - John Wards (johnwards) - Tomas Norkūnas (norkunas) + - Alexandre Daubois (alexandre-daubois) - Baptiste Clavié (talus) - - HypeMC (hypemc) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - - Alexandre Daubois (alexandre-daubois) - Vincent Langlet (deviling) + - Julien Falque (julienfalque) - Massimiliano Arione (garak) - Arnaud Le Blanc (arnaud-lb) - Przemysław Bogusz (przemyslaw-bogusz) - - Julien Falque (julienfalque) - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - Tomáš Votruba (tomas_votruba) @@ -143,6 +143,7 @@ The Symfony Connect username in parenthesis allows to get more information - Włodzimierz Gajda (gajdaw) - Christian Scheb - Adrien Brault (adrienbrault) + - Maxime Helias (maxhelias) - Yanick Witschi (toflar) - Jacob Dreesen (jdreesen) - Malte Schlüter (maltemaltesich) @@ -152,6 +153,7 @@ The Symfony Connect username in parenthesis allows to get more information - Teoh Han Hui (teohhanhui) - Colin Frei - Javier Spagnoletti (phansys) + - Ruud Kamphuis (ruudk) - Joshua Thijssen - Daniel Wehner (dawehner) - Tugdual Saunier (tucksaun) @@ -159,15 +161,13 @@ The Symfony Connect username in parenthesis allows to get more information - Gordon Franke (gimler) - Saif Eddin Gmati (azjezz) - Richard van Laak (rvanlaak) - - Maxime Helias (maxhelias) + - Gary PEGEOT (gary-p) - Jesse Rushlow (geeshoe) - Fabien Pennequin (fabienpennequin) - Olivier Dolbeau (odolbeau) - Smaine Milianni (ismail1432) - Eric GELOEN (gelo) - - Gary PEGEOT (gary-p) - Matthieu Napoli (mnapoli) - - Ruud Kamphuis (ruudk) - Ion Bazan (ionbazan) - Jannik Zschiesche (apfelbox) - Robert Schönthal (digitalkaoz) @@ -190,6 +190,7 @@ The Symfony Connect username in parenthesis allows to get more information - Albert Casademont (acasademont) - Arnaud Kleinpeter (nanocom) - Guilherme Blanco (guilhermeblanco) + - Marco Pivetta (ocramius) - SpacePossum - Alexander Menshchikov (zmey_kk) - Pablo Godel (pgodel) @@ -203,20 +204,23 @@ The Symfony Connect username in parenthesis allows to get more information - jwdeitch - Jeroen Spee (jeroens) - Jérôme Parmentier (lctrs) - - Marco Pivetta (ocramius) - Fabien Bourigault (fbourigault) - Joe Bennett (kralos) - Mikael Pajunen - Andreas Schempp (aschempp) - Alessandro Lai (jean85) - Romaric Drigon (romaricdrigon) + - Christopher Hertel (chertel) - Arman Hosseini (arman) + - Rokas Mikalkėnas (rokasm) - Niels Keurentjes (curry684) - Vyacheslav Pavlov + - Andreas Möller (localheinz) - Richard Shank (iampersistent) - Wouter J - Thomas Rabaix (rande) - Chi-teck + - Baptiste Leduc (korbeil) - Timo Bakx (timobakx) - Vincent Touzet (vincenttouzet) - Nate Wiebe (natewiebe13) @@ -225,16 +229,13 @@ The Symfony Connect username in parenthesis allows to get more information - Ben Davies (bendavies) - Clemens Tolboom - Helmer Aaviksoo - - Christopher Hertel (chertel) - Remon van de Kamp (rpkamp) - - Rokas Mikalkėnas (rokasm) - Filippo Tessarotto (slamdunk) - Hiromi Hishida (77web) - Michael Käfer (michael_kaefer) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - Dawid Nowak - - Andreas Möller (localheinz) - Roman Martinuk (a2a4) - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) @@ -244,7 +245,6 @@ The Symfony Connect username in parenthesis allows to get more information - Samuel NELA (snela) - David Prévot - Hugo Monteiro (monteiro) - - Baptiste Leduc (korbeil) - Dmitrii Poddubnyi (karser) - zairig imad (zairigimad) - Tien Vo (tienvx) @@ -275,6 +275,7 @@ The Symfony Connect username in parenthesis allows to get more information - Thibaut Cheymol (tcheymol) - Sebastien Morel (plopix) - mcfedr (mcfedr) + - Colin O'Dell (colinodell) - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - Baptiste Lafontaine (magnetik) @@ -311,7 +312,6 @@ The Symfony Connect username in parenthesis allows to get more information - Matthieu Auger (matthieuauger) - Leszek Prabucki (l3l0) - Nicolas Philippe (nikophil) - - Colin O'Dell (colinodell) - Emanuele Panzeri (thepanz) - François Zaninotto (fzaninotto) - Dustin Whittle (dustinwhittle) @@ -325,6 +325,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sven Paulus (subsven) - Daniel STANCU - Maxime Veber (nek-) + - Sylvain Fabre (sylfabre) - Loick Piera (pyrech) - Clara van Miert - Valentine Boineau (valentineboineau) @@ -340,6 +341,7 @@ The Symfony Connect username in parenthesis allows to get more information - Victor Bocharsky (bocharsky_bw) - Bozhidar Hristov (warxcell) - Marcel Beerta (mazen) + - Thomas Landauer (thomas-landauer) - Pavel Batanov (scaytrase) - Mantis Development - Loïc Faugeron @@ -373,10 +375,11 @@ The Symfony Connect username in parenthesis allows to get more information - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) - Mickaël Andrieu (mickaelandrieu) + - Soner Sayakci - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA - - Sylvain Fabre (sylfabre) + - Artem Lopata - Patrick McDougle (patrick-mcdougle) - Marc Weistroff (futurecat) - Alif Rachmawadi @@ -402,7 +405,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - jdhoek - - Thomas Landauer (thomas-landauer) - Jurica Vlahoviček (vjurica) - Bob den Otter (bopp) - Thomas Schulz (king2500) @@ -436,7 +438,6 @@ The Symfony Connect username in parenthesis allows to get more information - Wouter Van Hecke - Iker Ibarguren (ikerib) - Bob van de Vijver (bobvandevijver) - - Soner Sayakci - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Arjen van der Meijden @@ -459,6 +460,7 @@ The Symfony Connect username in parenthesis allows to get more information - Manuel Kiessling (manuelkiessling) - Dimitri Gritsajuk (ottaviano) - Alexey Kopytko (sanmai) + - Gijs van Lammeren - Pol Dellaiera (drupol) - Atsuhiro KUBO (iteman) - Alireza Mirsepassi (alirezamirsepassi) @@ -535,7 +537,6 @@ The Symfony Connect username in parenthesis allows to get more information - Christian Gärtner (dagardner) - Dmytro Borysovskyi (dmytr0) - Tomasz Kowalczyk (thunderer) - - Artem Lopata - Artur Eshenbrener - Thomas Perez (scullwm) - Yoann RENARD (yrenard) @@ -553,6 +554,7 @@ The Symfony Connect username in parenthesis allows to get more information - hossein zolfi (ocean) - Clément Gautier (clementgautier) - Koen Reiniers (koenre) + - Hugo Alliaume (kocal) - Sanpi - Eduardo Gulias (egulias) - giulio de donato (liuggio) @@ -560,10 +562,12 @@ The Symfony Connect username in parenthesis allows to get more information - ShinDarth - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) + - Rhodri Pugh (rodnaph) - Grzegorz Zdanowski (kiler129) - Kirill chEbba Chebunin (chebba) - - Fabien Villepinte + - SiD (plbsid) - Matthew Grasmick - Greg Thornton (xdissent) - BENOIT POLASZEK (bpolaszek) @@ -575,12 +579,12 @@ The Symfony Connect username in parenthesis allows to get more information - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) - Vyacheslav Salakhutdinov (megazoll) + - Antoine Lamirault - Phil Taylor (prazgod) - Hassan Amouhzi - Tamas Szijarto - Michele Locati - Pavel Volokitin (pvolok) - - Gijs van Lammeren - Arthur de Moulins (4rthem) - Matthias Althaus (althaus) - Nicolas Dewez (nicolas_dewez) @@ -650,6 +654,7 @@ The Symfony Connect username in parenthesis allows to get more information - scyzoryck - Matthias Krauser (mkrauser) - Erkhembayar Gantulga (erheme318) + - Alexis Lefebvre - Lorenzo Millucci (lmillucci) - Jérôme Tamarelle (jtamarelle-prismamedia) - Andrii Popov (andrii-popov) @@ -677,6 +682,7 @@ The Symfony Connect username in parenthesis allows to get more information - Chris Sedlmayr (catchamonkey) - Indra Gunawan (indragunawan) - Mathias STRASSER (roukmoute) + - simon chrzanowski (simonch) - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) @@ -719,7 +725,6 @@ The Symfony Connect username in parenthesis allows to get more information - Marek Zajac - Adam Harvey - Anton Bakai - - Rhodri Pugh (rodnaph) - battye - Sam Fleming (sam_fleming) - William Arslett @@ -749,7 +754,6 @@ The Symfony Connect username in parenthesis allows to get more information - Sebastian Bergmann - Miroslav Sustek - Pablo Díez (pablodip) - - SiD (plbsid) - Michel Roca (mroca) - Kevin McBride - Sergio Santoro @@ -780,6 +784,7 @@ The Symfony Connect username in parenthesis allows to get more information - Markus Lanthaler (lanthaler) - Remi Collet - Vicent Soria Durá (vicentgodella) + - Daniel Gorgan - Michael Moravec - Carlos Buenosvinos (carlosbuenosvinos) - Leevi Graham (leevigraham) @@ -799,6 +804,7 @@ The Symfony Connect username in parenthesis allows to get more information - Scott Arciszewski - Xavier HAUSHERR - Norbert Orzechowicz (norzechowicz) + - stlrnz - Denis Charrier (brucewouaigne) - Matthijs van den Bos (matthijs) - Simon Podlipsky (simpod) @@ -912,8 +918,8 @@ The Symfony Connect username in parenthesis allows to get more information - vitaliytv - Nicolas Martin (cocorambo) - Adrian Nguyen (vuphuong87) + - Khoo Yong Jun - Sebastian Blum - - Alexis Lefebvre - Laurent Clouet - aubx - Julien Turby @@ -930,6 +936,7 @@ The Symfony Connect username in parenthesis allows to get more information - pizzaminded - Stéphane Escandell (sescandell) - Konstantin S. M. Möllers (ksmmoellers) + - Fractal Zombie - linh - James Johnston - Sinan Eldem @@ -978,6 +985,7 @@ The Symfony Connect username in parenthesis allows to get more information - Evgeny Anisiforov - smoench - Max Grigorian (maxakawizard) + - Martins Sipenko - Guilherme Augusto Henschel - Rostyslav Kinash - Cristoforo Cervino (cristoforocervino) @@ -1021,9 +1029,11 @@ The Symfony Connect username in parenthesis allows to get more information - Tomas Javaisis - Ivan Grigoriev - Johann Saunier (prophet777) + - Kevin SCHNEKENBURGER - Fabien Salles (blacked) - Andreas Erhard - John VanDeWeghe + - Sergey Belyshkin - Michael Devery (mickadoo) - Antoine Corcy - Ahmed Ashraf (ahmedash95) @@ -1085,6 +1095,7 @@ The Symfony Connect username in parenthesis allows to get more information - Roromix - Maxime AILLOUD (mailloud) - Richard van den Brand (ricbra) + - Toon Verwerft (veewee) - mohammadreza honarkhah - develop - flip111 @@ -1191,6 +1202,7 @@ The Symfony Connect username in parenthesis allows to get more information - Julien Pauli - Dominik Piekarski (dompie) - Rares Sebastian Moldovan (raresmldvn) + - Jérémy REYNAUD (babeuloula) - Mathieu Rochette (mathroc) - Victor Garcia - Jérôme Tanghe (deuchnord) @@ -1239,7 +1251,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tito Costa - Jan Prieser - GDIBass - - Antoine Lamirault + - Maximilian Bösing - Thiago Melo - Adrien Lucas (adrienlucas) - Zhuravlev Alexander (scif) @@ -1300,7 +1312,6 @@ The Symfony Connect username in parenthesis allows to get more information - Loïc Beurlet - Sébastien COURJEAN - Ana Raro - - Daniel Gorgan - Ana Raro - Tony Malzhacker - Pchol @@ -1380,13 +1391,14 @@ The Symfony Connect username in parenthesis allows to get more information - Forfarle (forfarle) - Harry Walter (haswalt) - Johnson Page (jwpage) + - Kuba Werłos (kuba) - Ruben Gonzalez (rubenruateltek) - Michael Roterman (wtfzdotnet) + - Philipp Keck - Arno Geurts - Adán Lobato (adanlobato) - Ian Jenkins (jenkoian) - Kai Eichinger (kai_eichinger) - - Hugo Alliaume (kocal) - Marcos Gómez Vilches (markitosgv) - Matthew Davis (mdavis1982) - Paulo Ribeiro (paulo) @@ -1396,7 +1408,6 @@ The Symfony Connect username in parenthesis allows to get more information - Antoine LA - den - Pavol Tuka - - stlrnz - pawel-lewtak - omerida - Gábor Tóth @@ -1476,9 +1487,7 @@ The Symfony Connect username in parenthesis allows to get more information - neghmurken - xaav - Mahmoud Mostafa (mahmoud) - - Fractal Zombie - Ahmed Abdou - - Khoo Yong Jun - shreyadenny - Daniel Iwaniec - Pieter @@ -1509,6 +1518,7 @@ The Symfony Connect username in parenthesis allows to get more information - LHommet Nicolas (nicolaslh) - fabios - Sander Coolen (scoolen) + - Emil Masiakowski - Amirreza Shafaat (amirrezashafaat) - Adoni Pavlakis (adoni) - Nicolas Le Goff (nlegoff) @@ -1628,6 +1638,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jean-Guilhem Rouel (jean-gui) - Yoann MOROCUTTI - jfcixmedia + - Tomasz Kusy - Dominic Tubach - Nikita Konstantinov - Martijn Evers @@ -1635,6 +1646,7 @@ The Symfony Connect username in parenthesis allows to get more information - Philipp Fritsche - tarlepp - Benjamin Paap (benjaminpaap) + - Guillaume Aveline - Christian - Denis Golubovskiy (bukashk0zzz) - Arkadiusz Rzadkowolski (flies) @@ -1647,7 +1659,6 @@ The Symfony Connect username in parenthesis allows to get more information - hugofonseca (fonsecas72) - Marc Duboc (icemad) - Martynas Narbutas - - Toon Verwerft (veewee) - Bailey Parker - Eddie Jaoude - Antanas Arvasevicius @@ -1716,6 +1727,7 @@ The Symfony Connect username in parenthesis allows to get more information - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) - Neil Katin + - Oleg Mifle - David Otton - Will Donohoe - gnito-org @@ -1740,13 +1752,13 @@ The Symfony Connect username in parenthesis allows to get more information - Amine Yakoubi - Eduardo García Sanz (coma) - Sergio (deverad) - - simon chrzanowski (simonch) - Makdessi Alex - James Gilliland - fduch (fduch) - Juan Miguel Besada Vidal (soutlink) - dlorek - Stuart Fyfe + - Jason Schilling (chapterjason) - David de Boer (ddeboer) - Eno Mullaraj (emullaraj) - Nathan PAGE (nathix) @@ -1765,6 +1777,7 @@ The Symfony Connect username in parenthesis allows to get more information - Roger Webb - Dmitriy Simushev - Pawel Smolinski + - Simon Watiau (simonwatiau) - Oxan van Leeuwen - pkowalczyk - Soner Sayakci @@ -1815,6 +1828,7 @@ The Symfony Connect username in parenthesis allows to get more information - Dmitri Petmanson - heccjj - Alexandre Melard + - PierreRebeilleau - Jay Klehr - Sergey Yuferev - Tobias Stöckler @@ -1954,7 +1968,6 @@ The Symfony Connect username in parenthesis allows to get more information - Wojciech Błoszyk (wbloszyk) - Giorgio Premi - abunch - - Sergey Belyshkin - tamcy - Mikko Pesari - ncou @@ -1982,6 +1995,7 @@ The Symfony Connect username in parenthesis allows to get more information - Raphaëll Roussel - Tadcka - Beth Binkovitz + - Maxim Semkin - Gonzalo Míguez - Fabian Haase - Romain Geissler @@ -2027,6 +2041,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tony Vermeiren (tony) - Bart Wach - Jos Elstgeest + - Kirill Lazarev - Thomas Counsell - BilgeXA - r1pp3rj4ck @@ -2197,6 +2212,7 @@ The Symfony Connect username in parenthesis allows to get more information - Matt Farmer - catch - aetxebeste + - Vitali Tsyrkin - Juga Paazmaya - Alexandre Segura - afaricamp @@ -2249,6 +2265,7 @@ The Symfony Connect username in parenthesis allows to get more information - Andreas - Markus - agaktr + - Mostafa - kernig - Thomas Chmielowiec - shdev @@ -2276,6 +2293,7 @@ The Symfony Connect username in parenthesis allows to get more information - Christoph Nissle (derstoffel) - Denys Voronin (hurricane) - Ionel Scutelnicu (ionelscutelnicu) + - Juan Gonzalez Montes (juanwilde) - Mathieu Dewet (mdewet) - Nicolas Tallefourtané (nicolab) - Botond Dani (picur) @@ -2294,7 +2312,6 @@ The Symfony Connect username in parenthesis allows to get more information - Christopher Parotat - Dennis Haarbrink - me_shaon - - Maximilian Bösing - 蝦米 - Grayson Koonce (breerly) - Andrey Helldar (helldar) @@ -2373,6 +2390,7 @@ The Symfony Connect username in parenthesis allows to get more information - Cyril Pascal (paxal) - Cédric Dugat (ph3nol) - Philip Dahlstrøm (phidah) + - Pierre Rebeilleau (pierrereb) - Milos Colakovic (project2481) - Raphael de Almeida (raphaeldealmeida) - Rénald Casagraude (rcasagraude) @@ -2435,6 +2453,7 @@ The Symfony Connect username in parenthesis allows to get more information - Peter Breuls - Chansig - Tischoi + - divinity76 - Andreas Hasenack - J Bruni - Alexey Prilipko @@ -2468,6 +2487,7 @@ The Symfony Connect username in parenthesis allows to get more information - Pedro Magalhães (pmmaga) - Rares Vlaseanu (raresvla) - Sergii Dolgushev (serhey) + - Rein Baarsma (solidwebcode) - tante kinast (tante) - Stephen Lewis (tehanomalousone) - Ahmed Hannachi (tiecoders) @@ -2525,6 +2545,7 @@ The Symfony Connect username in parenthesis allows to get more information - grifx - Robert Campbell - Matt Lehner + - Olexandr Kalaidzhy - Helmut Januschka - Hein Zaw Htet™ - Ruben Kruiswijk @@ -2585,6 +2606,7 @@ The Symfony Connect username in parenthesis allows to get more information - Gerrit Drost - Linnaea Von Lavia - Bastien Clément + - Julius Šakalys - Javan Eskander - Lenar Lõhmus - Cristian Gonzalez @@ -2596,7 +2618,6 @@ The Symfony Connect username in parenthesis allows to get more information - Pavinthan - Sylvain METAYER - ddebree - - Kuba Werłos - Gyula Szucs - Tomas Liubinas - Ivo Valchev @@ -2664,7 +2685,6 @@ The Symfony Connect username in parenthesis allows to get more information - Geordie - Exploit.cz - GuillaumeVerdon - - Philipp Keck - Angel Fernando Quiroz Campos - Ondrej Mirtes - akimsko @@ -2726,6 +2746,7 @@ The Symfony Connect username in parenthesis allows to get more information - arend - Vincent Godé - Dusan Kasan + - helmi - Michael Steininger - Nardberjean - Karolis @@ -2865,6 +2886,7 @@ The Symfony Connect username in parenthesis allows to get more information - Babichev Maxim - Edvin Hultberg - Benjamin Long + - Kévin Gonella - Ben Miller - Peter Gribanov - Ash014 @@ -2896,6 +2918,7 @@ The Symfony Connect username in parenthesis allows to get more information - Steve Marvell - Dawid Nowak - Lesnykh Ilia + - Shyim - sabruss - darnel - Karolis Daužickas @@ -2943,6 +2966,7 @@ The Symfony Connect username in parenthesis allows to get more information - Christian Gripp (core23) - Christoph Schaefer (cvschaefer) - Damon Jones (damon__jones) + - Alexandre Fiocre (demos77) - Łukasz Giza (destroyer) - Daniel Londero (dlondero) - Sebastian Landwehr (dword123) diff --git a/LICENSE b/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md index 347263738c7b4..e70e3e5d2a917 100644 --- a/UPGRADE-6.0.md +++ b/UPGRADE-6.0.md @@ -124,7 +124,7 @@ 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` - * Retrieving non-scalar values using `InputBag::get()` will throw `BadRequestException` (use `InputBad::all()` instead to retrieve an array) + * Retrieving non-scalar values using `InputBag::get()` will throw `BadRequestException` (use `InputBag::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` * Passing `null` as `$requestIp` to `IpUtils::__checkIp()`, `IpUtils::__checkIp4()` or `IpUtils::__checkIp6()` is not supported anymore. diff --git a/composer.json b/composer.json index 978743d34d861..e34ed24a4d6d8 100644 --- a/composer.json +++ b/composer.json @@ -162,7 +162,6 @@ }, "config": { "allow-plugins": { - "composer/package-versions-deprecated": true, "symfony/runtime": true } }, diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index 4774019da3349..fc8910eb93f6d 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -137,10 +137,15 @@ protected function setMappingDriverConfig(array $mappingConfig, string $mappingN */ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, ContainerBuilder $container, string $bundleDir = null): array|false { - $bundleDir ??= \dirname($bundle->getFileName()); + $bundleClassDir = \dirname($bundle->getFileName()); + $bundleDir ??= $bundleClassDir; if (!$bundleConfig['type']) { $bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container); + + if (!$bundleConfig['type'] && $bundleDir !== $bundleClassDir) { + $bundleConfig['type'] = $this->detectMetadataDriver($bundleClassDir, $container); + } } if (!$bundleConfig['type']) { @@ -150,7 +155,7 @@ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \Re if (!$bundleConfig['dir']) { if (\in_array($bundleConfig['type'], ['annotation', 'staticphp', 'attribute'])) { - $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName(); + $bundleConfig['dir'] = $bundleClassDir.'/'.$this->getMappingObjectDefaultName(); } else { $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory($bundleDir); } diff --git a/src/Symfony/Bridge/Doctrine/LICENSE b/src/Symfony/Bridge/Doctrine/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index eedb55077605a..9e294b2fdbd30 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -135,14 +135,17 @@ public function getTypes(string $class, string $property, array $context = []): } if ($metadata->hasField($property)) { + $nullable = $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property); + if (null !== $enumClass = $metadata->getFieldMapping($property)['enumType'] ?? null) { + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $enumClass)]; + } + $typeOfField = $metadata->getTypeOfField($property); if (!$builtinType = $this->getPhpType($typeOfField)) { return null; } - $nullable = $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property); - switch ($builtinType) { case Type::BUILTIN_TYPE_OBJECT: switch ($typeOfField) { diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 774f1f0babf3c..17df373369d95 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; /** * @author Fabio B. Silva @@ -53,6 +54,10 @@ protected function setUp(): void $this->extension ->method('getMappingObjectDefaultName') ->willReturn('Entity'); + + $this->extension + ->method('getMappingResourceExtension') + ->willReturn('orm'); } public function testFixManagersAutoMappingsWithTwoAutomappings() @@ -271,6 +276,73 @@ public function testUnrecognizedCacheDriverException() $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); } + public function providerBundles() + { + yield ['AnnotationsBundle', 'annotation', '/Entity']; + yield ['AttributesBundle', 'attribute', '/Entity']; + yield ['XmlBundle', 'xml', '/Resources/config/doctrine']; + yield ['PhpBundle', 'php', '/Resources/config/doctrine']; + yield ['YamlBundle', 'yml', '/Resources/config/doctrine']; + + yield ['SrcXmlBundle', 'xml', '/Resources/config/doctrine']; + + yield ['NewAnnotationsBundle', 'annotation', \DIRECTORY_SEPARATOR.'src/Entity']; + yield ['NewXmlBundle', 'xml', '/config/doctrine']; + } + + /** + * @dataProvider providerBundles + */ + public function testBundleAutoMapping(string $bundle, string $expectedType, string $dirSuffix) + { + $bundleDir = __DIR__.'/../Fixtures/Bundles/'.$bundle; + $bundleClassName = 'Fixtures\\Bundles\\'.$bundle.'\\'.$bundle; + + if (is_dir($bundleDir.'/src')) { + require_once $bundleDir.'/src/'.$bundle.'.php'; + } else { + require_once $bundleDir.'/'.$bundle.'.php'; + } + + /** @var BundleInterface $bundleClass */ + $bundleClass = new $bundleClassName(); + + $mappingConfig = [ + 'dir' => false, + 'type' => false, + 'prefix' => false, + 'mapping' => true, + 'is_bundle' => true, + ]; + + $this->extension + ->method('getMappingResourceConfigDirectory') + ->willReturnCallback(function ($bundleDir) { + if (null !== $bundleDir && is_dir($bundleDir.'/config/doctrine')) { + return 'config/doctrine'; + } + + return 'Resources/config/doctrine'; + }); + + $container = $this->createContainer([], [$bundle => $bundleClassName]); + + $reflection = new \ReflectionClass(\get_class($this->extension)); + $method = $reflection->getMethod('getMappingDriverBundleConfigDefaults'); + $method->setAccessible(true); + + $this->assertSame( + [ + 'dir' => $bundleClass->getPath().$dirSuffix, + 'type' => $expectedType, + 'prefix' => $bundleClass->getNamespace().'\\Entity', + 'mapping' => true, + 'is_bundle' => true, + ], + $method->invoke($this->extension, $mappingConfig, new \ReflectionClass($bundleClass), $container, $bundleClass->getPath()) + ); + } + protected function invokeLoadCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName) { $method = new \ReflectionMethod($this->extension, 'loadObjectManagerCacheDriver'); @@ -280,10 +352,10 @@ protected function invokeLoadCacheDriver(array $objectManager, ContainerBuilder $method->invokeArgs($this->extension, [$objectManager, $container, $cacheName]); } - protected function createContainer(array $data = []): ContainerBuilder + protected function createContainer(array $data = [], array $extraBundles = []): ContainerBuilder { return new ContainerBuilder(new ParameterBag(array_merge([ - 'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'], + 'kernel.bundles' => array_merge(['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'], $extraBundles), 'kernel.cache_dir' => __DIR__, 'kernel.build_dir' => __DIR__, 'kernel.container_class' => 'kernel', diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php new file mode 100644 index 0000000000000..e4dfd3e07cc88 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AnnotationsBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php new file mode 100644 index 0000000000000..0d7cc91362da3 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +/** + * @Entity + */ +class Person +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AttributesBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AttributesBundle.php new file mode 100644 index 0000000000000..686dbe4e8f3b2 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AttributesBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AttributesBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AttributesBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/Entity/Person.php new file mode 100644 index 0000000000000..6b445b198457f --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/Entity/Person.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AttributesBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +#[Entity] +class Person +{ + #[Id, Column(type: 'integer')] + protected $id; + + #[Column(type: 'string')] + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php new file mode 100644 index 0000000000000..e94a24e1a95c7 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\NewAnnotationsBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +/** + * @Entity + */ +class Person +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/NewAnnotationsBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/NewAnnotationsBundle.php new file mode 100644 index 0000000000000..962b6d025ebc8 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/NewAnnotationsBundle.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\NewAnnotationsBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class NewAnnotationsBundle extends Bundle +{ + public function getPath(): string + { + return \dirname(__DIR__); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/config/doctrine/Person.orm.xml b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/config/doctrine/Person.orm.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/src/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/src/Entity/Person.php new file mode 100644 index 0000000000000..3adfa62aa90fe --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/src/Entity/Person.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\NewXmlBundle\Entity; + +class Person +{ + protected $id; + + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/src/NewXmlBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/src/NewXmlBundle.php new file mode 100644 index 0000000000000..b5abbdb38d45d --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewXmlBundle/src/NewXmlBundle.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\NewXmlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class NewXmlBundle extends Bundle +{ + public function getPath(): string + { + return \dirname(__DIR__); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/Entity/Person.php new file mode 100644 index 0000000000000..67937cd3b8bd4 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/Entity/Person.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\PhpBundle\Entity; + +class Person +{ + protected $id; + + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/PhpBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/PhpBundle.php new file mode 100644 index 0000000000000..0fbd8f34dd644 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/PhpBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\PhpBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class PhpBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/Resources/config/doctrine/Person.orm.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/PhpBundle/Resources/config/doctrine/Person.orm.php new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/Entity/Person.php new file mode 100644 index 0000000000000..445d0d4bd01ab --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/Entity/Person.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\SrcXmlBundle\Entity; + +class Person +{ + protected $id; + + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/Resources/config/doctrine/Person.orm.xml b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/Resources/config/doctrine/Person.orm.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/SrcXmlBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/SrcXmlBundle.php new file mode 100644 index 0000000000000..456983db04120 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/SrcXmlBundle/src/SrcXmlBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\SrcXmlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class SrcXmlBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/Entity/Person.php new file mode 100644 index 0000000000000..83c89773e4911 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/Entity/Person.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\XmlBundle\Entity; + +class Person +{ + protected $id; + + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/Resources/config/doctrine/Person.orm.xml b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/Resources/config/doctrine/Person.orm.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/XmlBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/XmlBundle.php new file mode 100644 index 0000000000000..6a69bd7583dd2 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/XmlBundle/XmlBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\XmlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class XmlBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/Entity/Person.php new file mode 100644 index 0000000000000..861cf5b652ab2 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/Entity/Person.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\YamlBundle\Entity; + +class Person +{ + protected $id; + + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/Resources/config/doctrine/Person.orm.yml b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/Resources/config/doctrine/Person.orm.yml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/YamlBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/YamlBundle.php new file mode 100644 index 0000000000000..415db47843d9d --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/YamlBundle/YamlBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\YamlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class YamlBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php new file mode 100644 index 0000000000000..8ac883e89c4a2 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class DoctrineLoaderEnum +{ + /** + * @ORM\Id + * @ORM\Column + */ + public $id; + + /** + * @ORM\Column(type="string", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString", length=1) + */ + public $enumString; + + /** + * @ORM\Column(type="integer", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") + */ + public $enumInt; +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 3b9419d174a60..5d0a09f5f906a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -14,12 +14,16 @@ use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Tools\Setup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor; use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineEnum; use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineGeneratedValue; use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString; use Symfony\Component\PropertyInfo\Type; /** @@ -124,6 +128,18 @@ public function testExtractWithEmbedded() $this->assertEquals($expectedTypes, $actualTypes); } + /** + * @requires PHP 8.1 + */ + public function testExtractEnum() + { + if (!property_exists(Column::class, 'enumType')) { + $this->markTestSkipped('The "enumType" requires doctrine/orm 2.11.'); + } + $this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, EnumString::class)], $this->createExtractor()->getTypes(DoctrineEnum::class, 'enumString', [])); + $this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, EnumInt::class)], $this->createExtractor()->getTypes(DoctrineEnum::class, 'enumInt', [])); + } + public function typesProvider() { $provider = [ diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php new file mode 100644 index 0000000000000..467522cbd3914 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** + * @Entity + */ +class DoctrineEnum +{ + /** + * @Id + * @Column(type="smallint") + */ + public $id; + + /** + * @Column(type="string", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString") + */ + protected $enumString; + + /** + * @Column(type="integer", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") + */ + protected $enumInt; +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/EnumInt.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/EnumInt.php new file mode 100644 index 0000000000000..c9560073fa611 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/EnumInt.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; + +enum EnumInt: int +{ + case Foo = 0; + case Bar = 1; +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/EnumString.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/EnumString.php new file mode 100644 index 0000000000000..0b6ef0df1bd41 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/EnumString.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; + +enum EnumString: string +{ + case Foo = 'f'; + case Bar = 'b'; +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php index 1ba0b6f254874..0bb9591e185df 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php @@ -11,11 +11,13 @@ namespace Symfony\Bridge\Doctrine\Tests\Validator; +use Doctrine\ORM\Mapping\Column; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\Fixtures\BaseUser; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderEmbed; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderEnum; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderNestedEmbed; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderNoAutoMappingEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderParentEntity; @@ -149,6 +151,31 @@ public function testLoadClassMetadata() $this->assertSame(AutoMappingStrategy::DISABLED, $noAutoMappingMetadata[0]->getAutoMappingStrategy()); } + /** + * @requires PHP 8.1 + */ + public function testExtractEnum() + { + if (!property_exists(Column::class, 'enumType')) { + $this->markTestSkipped('The "enumType" requires doctrine/orm 2.11.'); + } + + $validator = Validation::createValidatorBuilder() + ->addMethodMapping('loadValidatorMetadata') + ->enableAnnotationMapping() + ->addLoader(new DoctrineLoader(DoctrineTestHelper::createTestEntityManager(), '{^Symfony\\\\Bridge\\\\Doctrine\\\\Tests\\\\Fixtures\\\\DoctrineLoader}')) + ->getValidator() + ; + + $classMetadata = $validator->getMetadataFor(new DoctrineLoaderEnum()); + + $enumStringMetadata = $classMetadata->getPropertyMetadata('enumString'); + $this->assertCount(0, $enumStringMetadata); // asserts the length constraint is not added to an enum + + $enumStringMetadata = $classMetadata->getPropertyMetadata('enumInt'); + $this->assertCount(0, $enumStringMetadata); // asserts the length constraint is not added to an enum + } + public function testFieldMappingsConfiguration() { $validator = Validation::createValidatorBuilder() diff --git a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php index 238118aaad1a4..d1918fc6de45b 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php +++ b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php @@ -16,6 +16,7 @@ use Doctrine\ORM\Mapping\MappingException as OrmMappingException; use Doctrine\Persistence\Mapping\MappingException; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Mapping\AutoMappingStrategy; @@ -99,7 +100,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool $loaded = true; } - if (null === ($mapping['length'] ?? null) || !\in_array($mapping['type'], ['string', 'text'], true)) { + if (null === ($mapping['length'] ?? null) || null !== ($mapping['enumType'] ?? null) || !\in_array($mapping['type'], ['string', 'text'], true)) { continue; } diff --git a/src/Symfony/Bridge/Monolog/LICENSE b/src/Symfony/Bridge/Monolog/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bridge/Monolog/LICENSE +++ b/src/Symfony/Bridge/Monolog/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index c2a524c44f699..2aeae49d58fa9 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -408,11 +408,11 @@ private static function hasColorSupport() } if (\function_exists('stream_isatty')) { - return stream_isatty(\STDOUT); + return @stream_isatty(\STDOUT); } if (\function_exists('posix_isatty')) { - return posix_isatty(\STDOUT); + return @posix_isatty(\STDOUT); } $stat = fstat(\STDOUT); diff --git a/src/Symfony/Bridge/PhpUnit/LICENSE b/src/Symfony/Bridge/PhpUnit/LICENSE index c1f0aac1c5614..a843ec124ea70 100644 --- a/src/Symfony/Bridge/PhpUnit/LICENSE +++ b/src/Symfony/Bridge/PhpUnit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2021 Fabien Potencier +Copyright (c) 2014-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/ProxyManager/LICENSE b/src/Symfony/Bridge/ProxyManager/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bridge/ProxyManager/LICENSE +++ b/src/Symfony/Bridge/ProxyManager/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Twig/LICENSE b/src/Symfony/Bridge/Twig/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bridge/Twig/LICENSE +++ b/src/Symfony/Bridge/Twig/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/DebugBundle/LICENSE b/src/Symfony/Bundle/DebugBundle/LICENSE index c1f0aac1c5614..a843ec124ea70 100644 --- a/src/Symfony/Bundle/DebugBundle/LICENSE +++ b/src/Symfony/Bundle/DebugBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2021 Fabien Potencier +Copyright (c) 2014-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index 47067ebbab6a3..a46a0005dca40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -94,8 +94,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { /** @var KernelInterface $kernel */ $kernel = $this->getApplication()->getKernel(); - $targetArg = rtrim($input->getArgument('target'), '/'); - + $targetArg = rtrim($input->getArgument('target') ?? '', '/'); if (!$targetArg) { $targetArg = $this->getPublicDirectory($kernel->getContainer()); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 211ed9077839d..d9a52b204f5d1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -330,34 +330,10 @@ public function load(array $configs, ContainerBuilder $container) } } - // register cache before session so both can share the connection services - $this->registerCacheConfiguration($config['cache'], $container); - - if ($this->isConfigEnabled($container, $config['session'])) { - if (!\extension_loaded('session')) { - throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.'); - } - - $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'); - } - if ($this->isConfigEnabled($container, $config['request'])) { $this->registerRequestConfiguration($config['request'], $container, $loader); } - if (null === $config['csrf_protection']['enabled']) { - $config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']); - } - $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); - if ($this->isConfigEnabled($container, $config['form'])) { if (!class_exists(Form::class)) { throw new LogicException('Form support cannot be enabled as the Form component is not installed. Try running "composer require symfony/form".'); @@ -488,6 +464,31 @@ public function load(array $configs, ContainerBuilder $container) $this->registerUidConfiguration($config['uid'], $container, $loader); } + // register cache before session so both can share the connection services + $this->registerCacheConfiguration($config['cache'], $container); + + if ($this->isConfigEnabled($container, $config['session'])) { + if (!\extension_loaded('session')) { + throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.'); + } + + $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'); + } + + // csrf depends on session being registered + if (null === $config['csrf_protection']['enabled']) { + $config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']); + } + $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); + $this->addAnnotatedClassesToCompile([ '**\\Controller\\', '**\\Entity\\', diff --git a/src/Symfony/Bundle/FrameworkBundle/LICENSE b/src/Symfony/Bundle/FrameworkBundle/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/LICENSE +++ b/src/Symfony/Bundle/FrameworkBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 36353c7019ee7..1af10c4fbf6f4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -240,6 +240,6 @@ ->set('notifier.transport_factory.expo', ExpoTransportFactory::class) ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') + ->tag('texter.transport_factory') ; }; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php index 50201a3626dab..5655c05a26e35 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php @@ -97,6 +97,7 @@ ->tag('serializer.normalizer', ['priority' => -910]) ->set('serializer.normalizer.json_serializable', JsonSerializableNormalizer::class) + ->args([null, null]) ->tag('serializer.normalizer', ['priority' => -900]) ->set('serializer.normalizer.problem', ProblemNormalizer::class) @@ -176,6 +177,7 @@ ->tag('serializer.encoder') ->set('serializer.encoder.yaml', YamlEncoder::class) + ->args([null, null]) ->tag('serializer.encoder') ->set('serializer.encoder.csv', CsvEncoder::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml index 7878c2ecb68d4..3721de1cac584 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml @@ -2,7 +2,10 @@ imports: - { resource: ../config/default.yml } framework: - serializer: { enabled: true } + serializer: + enabled: true + default_context: + enable_max_depth: true property_info: { enabled: true } services: diff --git a/src/Symfony/Bundle/SecurityBundle/LICENSE b/src/Symfony/Bundle/SecurityBundle/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bundle/SecurityBundle/LICENSE +++ b/src/Symfony/Bundle/SecurityBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/TwigBundle/LICENSE b/src/Symfony/Bundle/TwigBundle/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bundle/TwigBundle/LICENSE +++ b/src/Symfony/Bundle/TwigBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/WebProfilerBundle/LICENSE b/src/Symfony/Bundle/WebProfilerBundle/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/LICENSE +++ b/src/Symfony/Bundle/WebProfilerBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Asset/LICENSE b/src/Symfony/Component/Asset/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Asset/LICENSE +++ b/src/Symfony/Component/Asset/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/BrowserKit/LICENSE b/src/Symfony/Component/BrowserKit/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/BrowserKit/LICENSE +++ b/src/Symfony/Component/BrowserKit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Cache/LICENSE b/src/Symfony/Component/Cache/LICENSE index 3796612f43c2b..7fa9539054928 100644 --- a/src/Symfony/Component/Cache/LICENSE +++ b/src/Symfony/Component/Cache/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2021 Fabien Potencier +Copyright (c) 2016-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterAndRedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterAndRedisAdapterTest.php index 46516e0095e6e..37282e8fceee8 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterAndRedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterAndRedisAdapterTest.php @@ -50,6 +50,7 @@ static function (CacheItem $item, $expiry) { ); $cache = $this->createCachePool(1); + $cache->clear(); $value = rand(); $item = $cache->getItem('foo'); $setCacheItemExpiry($item, 0); diff --git a/src/Symfony/Component/Cache/Traits/ContractsTrait.php b/src/Symfony/Component/Cache/Traits/ContractsTrait.php index debaeacc45ad0..1b2f21858ab7f 100644 --- a/src/Symfony/Component/Cache/Traits/ContractsTrait.php +++ b/src/Symfony/Component/Cache/Traits/ContractsTrait.php @@ -31,7 +31,10 @@ trait ContractsTrait doGet as private contractsGet; } - private \Closure $callbackWrapper; + /** + * @var callable + */ + private $callbackWrapper; private array $computing = []; /** @@ -42,7 +45,7 @@ trait ContractsTrait public function setCallbackWrapper(?callable $callbackWrapper): callable { if (!isset($this->callbackWrapper)) { - $this->callbackWrapper = \Closure::fromCallable([LockRegistry::class, 'compute']); + $this->callbackWrapper = [LockRegistry::class, 'compute']; if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { $this->setCallbackWrapper(null); diff --git a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php index 40d6f43a1aedd..8e30a552b0036 100644 --- a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php +++ b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php @@ -109,7 +109,7 @@ private function write(string $file, string $data, int $expiresAt = null) fclose($h); if (null !== $expiresAt) { - touch($this->tmp, $expiresAt); + touch($this->tmp, $expiresAt ?: time() + 31556952); // 1 year in seconds } return rename($this->tmp, $file); diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index fa96679028e71..a227449ca3c6b 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -180,7 +180,7 @@ public static function createConnection(string $dsn, array $options = []): \Redi $initializer = static function ($redis) use ($connect, $params, $dsn, $auth, $hosts, $tls) { $host = $hosts[0]['host'] ?? $hosts[0]['path']; - $port = $hosts[0]['port'] ?? null; + $port = $hosts[0]['port'] ?? 6379; if (isset($hosts[0]['host']) && $tls) { $host = 'tls://'.$host; diff --git a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php index 22f8280d6018a..a9f158e8ea259 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -90,8 +90,10 @@ public function ifNull(): static /** * Tests if the value is empty. + * + * @return $this */ - public function ifEmpty(): self + public function ifEmpty(): static { $this->ifPart = function ($v) { return empty($v); }; diff --git a/src/Symfony/Component/Config/LICENSE b/src/Symfony/Component/Config/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Config/LICENSE +++ b/src/Symfony/Component/Config/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 09234f5eb55b7..5a2323db605e9 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -953,6 +953,16 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); } + if (Terminal::hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + foreach ([\SIGINT, \SIGTERM] as $signal) { + $this->signalRegistry->register($signal, static function () use ($sttyMode) { + shell_exec('stty '.$sttyMode); + }); + } + } + if ($this->dispatcher) { foreach ($this->signalsToDispatchEvent as $signal) { $event = new ConsoleSignalEvent($command, $input, $output, $signal); diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index c914a12461288..7a09956e2119a 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -34,11 +34,11 @@ public function __clone() } /** - * Escapes "<" special char in given text. + * Escapes "<" and ">" special chars in given text. */ public static function escape(string $text): string { - $text = preg_replace('/([^\\\\]?)])/', '$1\\\\$2', $text); return self::escapeTrailingBackslash($text); } @@ -140,9 +140,10 @@ public function formatAndWrap(?string $message, int $width) { $offset = 0; $output = ''; - $tagRegex = '[a-z][^<>]*+'; + $openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*'; + $closeTagRegex = '[a-z][^<>]*+'; $currentLineLength = 0; - preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE); + preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE); foreach ($matches[0] as $i => $match) { $pos = $match[1]; $text = $match[0]; @@ -176,11 +177,7 @@ public function formatAndWrap(?string $message, int $width) $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength); - if (str_contains($output, "\0")) { - return strtr($output, ["\0" => '\\', '\\<' => '<']); - } - - return str_replace('\\<', '<', $output); + return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']); } public function getStyleStack(): OutputFormatterStyleStack @@ -211,7 +208,8 @@ private function createStyleFromString(string $string): ?OutputFormatterStyleInt } elseif ('bg' == $match[0]) { $style->setBackground(strtolower($match[1])); } elseif ('href' === $match[0]) { - $style->setHref($match[1]); + $url = preg_replace('{\\\\([<>])}', '$1', $match[1]); + $style->setHref($url); } elseif ('options' === $match[0]) { preg_match_all('([^,;]+)', strtolower($match[1]), $options); $options = array_shift($options); diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 3266a92dfa962..2e1ccb2abc7d0 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -246,6 +246,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu $numMatches = \count($matches); $sttyMode = shell_exec('stty -g'); + $isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null); + $r = [$inputStream]; + $w = []; // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) shell_exec('stty -icanon -echo'); @@ -255,11 +258,15 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu // Read a keypress while (!feof($inputStream)) { + while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) { + // Give signal handlers a chance to run + $r = [$inputStream]; + } $c = fread($inputStream, 1); // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { - shell_exec(sprintf('stty %s', $sttyMode)); + shell_exec('stty '.$sttyMode); throw new MissingInputException('Aborted.'); } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { @@ -364,7 +371,7 @@ function ($match) use ($ret) { } // Reset stty so it behaves normally again - shell_exec(sprintf('stty %s', $sttyMode)); + shell_exec('stty '.$sttyMode); return $fullChoice; } @@ -425,7 +432,7 @@ private function getHiddenResponse(OutputInterface $output, $inputStream, bool $ $value = fgets($inputStream, 4096); if (self::$stty && Terminal::hasSttyAvailable()) { - shell_exec(sprintf('stty %s', $sttyMode)); + shell_exec('stty '.$sttyMode); } if (false === $value) { @@ -478,11 +485,11 @@ private function isInteractiveInput($inputStream): bool } if (\function_exists('stream_isatty')) { - return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r')); + return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r')); } if (\function_exists('posix_isatty')) { - return self::$stdinIsInteractive = posix_isatty(fopen('php://stdin', 'r')); + return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); } if (!\function_exists('exec')) { diff --git a/src/Symfony/Component/Console/Input/StringInput.php b/src/Symfony/Component/Console/Input/StringInput.php index eb5c07fddffb4..76f1d5030a7a7 100644 --- a/src/Symfony/Component/Console/Input/StringInput.php +++ b/src/Symfony/Component/Console/Input/StringInput.php @@ -24,7 +24,7 @@ */ class StringInput extends ArgvInput { - public const REGEX_STRING = '([^\s]+?)(?:\s|(?hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); + if (!$this->hasStderrSupport()) { + return fopen('php://output', 'w'); + } + + // Use STDERR when possible to prevent from opening too many file descriptors + return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w')); } } diff --git a/src/Symfony/Component/Console/Question/ChoiceQuestion.php b/src/Symfony/Component/Console/Question/ChoiceQuestion.php index 97121754c2d95..e449ff683d20a 100644 --- a/src/Symfony/Component/Console/Question/ChoiceQuestion.php +++ b/src/Symfony/Component/Console/Question/ChoiceQuestion.php @@ -119,18 +119,18 @@ private function getDefaultValidator(): callable return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { if ($multiselect) { // Check for a separated comma values - if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selected, $matches)) { + if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) { throw new InvalidArgumentException(sprintf($errorMessage, $selected)); } - $selectedChoices = explode(',', $selected); + $selectedChoices = explode(',', (string) $selected); } else { $selectedChoices = [$selected]; } if ($this->isTrimmable()) { foreach ($selectedChoices as $k => $v) { - $selectedChoices[$k] = trim($v); + $selectedChoices[$k] = trim((string) $v); } } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 619009efe29cb..c5acd5c8666fe 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -38,9 +38,11 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\SignalRegistry\SignalRegistry; +use Symfony\Component\Console\Terminal; use Symfony\Component\Console\Tester\ApplicationTester; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Process\Process; class ApplicationTest extends TestCase { @@ -1882,6 +1884,39 @@ public function testSignalableCommandInterfaceWithoutSignals() $application->add($command); $this->assertSame(0, $application->run(new ArrayInput(['signal']))); } + + /** + * @group tty + */ + public function testSignalableRestoresStty() + { + if (!Terminal::hasSttyAvailable()) { + $this->markTestSkipped('stty not available'); + } + + if (!SignalRegistry::isSupported()) { + $this->markTestSkipped('pcntl signals not available'); + } + + $previousSttyMode = shell_exec('stty -g'); + + $p = new Process(['php', __DIR__.'/Fixtures/application_signalable.php']); + $p->setTty(true); + $p->start(); + + for ($i = 0; $i < 10 && shell_exec('stty -g') === $previousSttyMode; ++$i) { + usleep(100000); + } + + $this->assertNotSame($previousSttyMode, shell_exec('stty -g')); + $p->signal(\SIGINT); + $p->wait(); + + $sttyMode = shell_exec('stty -g'); + shell_exec('stty '.$previousSttyMode); + + $this->assertSame($previousSttyMode, $sttyMode); + } } class CustomApplication extends Application diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_signalable.php b/src/Symfony/Component/Console/Tests/Fixtures/application_signalable.php new file mode 100644 index 0000000000000..0194703b2fe01 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_signalable.php @@ -0,0 +1,36 @@ +setCode(function(InputInterface $input, OutputInterface $output) { + $this->getHelper('question') + ->ask($input, $output, new ChoiceQuestion('😊', ['y'])); + + return 0; + }) + ->run() + +; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt b/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt index 45e7bec4d9d7e..fcab77a29e19a 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt @@ -2,9 +2,9 @@ command 2 description Usage: - descriptor:command2 [options] [--] \ - descriptor:command2 -o|--option_name \ - descriptor:command2 \ + descriptor:command2 [options] [--] \ + descriptor:command2 -o|--option_name \ + descriptor:command2 \ Arguments: argument_name diff --git a/src/Symfony/Component/Console/Tests/Fixtures/command_mbstring.txt b/src/Symfony/Component/Console/Tests/Fixtures/command_mbstring.txt index 2fd51d057cf62..1fa4e3135fa23 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/command_mbstring.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/command_mbstring.txt @@ -2,9 +2,9 @@ command åèä description Usage: - descriptor:åèä [options] [--] \ - descriptor:åèä -o|--option_name \ - descriptor:åèä \ + descriptor:åèä [options] [--] \ + descriptor:åèä -o|--option_name \ + descriptor:åèä \ Arguments: argument_åèä diff --git a/src/Symfony/Component/Console/Tests/Fixtures/input_argument_with_style.txt b/src/Symfony/Component/Console/Tests/Fixtures/input_argument_with_style.txt index 35384a6be87e7..79149ca69866a 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/input_argument_with_style.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/input_argument_with_style.txt @@ -1 +1 @@ - argument_name argument description [default: "\style\"] + argument_name argument description [default: "\style\"] diff --git a/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style.txt b/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style.txt index 880a53518e214..4bd30a662f4c3 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style.txt @@ -1 +1 @@ - -o, --option_name=OPTION_NAME option description [default: "\style\"] + -o, --option_name=OPTION_NAME option description [default: "\style\"] diff --git a/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style_array.txt b/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style_array.txt index 265c18c5a45d2..1fbb05b8a9ec7 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style_array.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/input_option_with_style_array.txt @@ -1 +1 @@ - -o, --option_name=OPTION_NAME option description [default: ["\Hello\","\world\"]] (multiple values allowed) + -o, --option_name=OPTION_NAME option description [default: ["\Hello\","\world\"]] (multiple values allowed) diff --git a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php index 1a6aae23ef228..3ad18b8978b6c 100644 --- a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php +++ b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php @@ -32,7 +32,10 @@ public function testLGCharEscaping() $this->assertEquals('foo << bar \\', $formatter->format('foo << bar \\')); $this->assertEquals("foo << \033[32mbar \\ baz\033[39m \\", $formatter->format('foo << bar \\ baz \\')); $this->assertEquals('some info', $formatter->format('\\some info\\')); - $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); + $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); + // every < and > gets escaped if not already escaped, but already escaped ones do not get escaped again + // and escaped backslashes remain as such, same with backslashes escaping non-special characters + $this->assertEquals('foo \\< bar \\< baz \\\\< foo \\> bar \\> baz \\\\> \\x', OutputFormatter::escape('foo < bar \\< baz \\\\< foo > bar \\> baz \\\\> \\x')); $this->assertEquals( "\033[33mSymfony\\Component\\Console does work very well!\033[39m", @@ -264,6 +267,7 @@ public function provideDecoratedAndNonDecoratedOutput() ['some question', 'some question', "\033[30;46msome question\033[39;49m"], ['some text with inline style', 'some text with inline style', "\033[31msome text with inline style\033[39m"], ['some URL', 'some URL', "\033]8;;idea://open/?file=/path/SomeFile.php&line=12\033\\some URL\033]8;;\033\\"], + ['>some URL with \', 'some URL with ', "\033]8;;https://example.com/\033\\some URL with \033]8;;\033\\"], ['some URL', 'some URL', 'some URL', 'JetBrains-JediTerm'], ]; } diff --git a/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php index 934e11ac1b0a3..c9a3c5e001d7a 100644 --- a/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php @@ -83,9 +83,9 @@ public function testFormatBlockLGEscaping() $formatter = new FormatterHelper(); $this->assertEquals( - ' '."\n". - ' \some info\ '."\n". - ' ', + ' '."\n". + ' \some info\ '."\n". + ' ', $formatter->formatBlock('some info', 'error', true), '::formatBlock() escapes \'<\' chars' ); diff --git a/src/Symfony/Component/Console/Tests/Input/StringInputTest.php b/src/Symfony/Component/Console/Tests/Input/StringInputTest.php index f781b7ccfa174..2bd40dec9c365 100644 --- a/src/Symfony/Component/Console/Tests/Input/StringInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/StringInputTest.php @@ -71,6 +71,7 @@ public function getTokenizeData() ["--long-option='foo bar''another'", ['--long-option=foo baranother'], '->tokenize() parses long options with a value'], ["--long-option='foo bar'\"another\"", ['--long-option=foo baranother'], '->tokenize() parses long options with a value'], ['foo -a -ffoo --long bar', ['foo', '-a', '-ffoo', '--long', 'bar'], '->tokenize() parses when several arguments and options'], + ["--arg=\\\"'Jenny'\''s'\\\"", ["--arg=\"Jenny's\""], '->tokenize() parses quoted quotes'], ]; } diff --git a/src/Symfony/Component/Console/Tests/Question/ChoiceQuestionTest.php b/src/Symfony/Component/Console/Tests/Question/ChoiceQuestionTest.php index 8a48cfcf169b1..8de26419f9108 100644 --- a/src/Symfony/Component/Console/Tests/Question/ChoiceQuestionTest.php +++ b/src/Symfony/Component/Console/Tests/Question/ChoiceQuestionTest.php @@ -19,14 +19,15 @@ class ChoiceQuestionTest extends TestCase /** * @dataProvider selectUseCases */ - public function testSelectUseCases($multiSelect, $answers, $expected, $message) + public function testSelectUseCases($multiSelect, $answers, $expected, $message, $default = null) { $question = new ChoiceQuestion('A question', [ 'First response', 'Second response', 'Third response', 'Fourth response', - ]); + null, + ], $default); $question->setMultiselect($multiSelect); @@ -59,6 +60,19 @@ public function selectUseCases() ['First response', 'Second response'], 'When passed multiple answers on MultiSelect, the defaultValidator must return these answers as an array', ], + [ + false, + [null], + null, + 'When used null as default single answer on singleSelect, the defaultValidator must return this answer as null', + ], + [ + false, + ['First response'], + 'First response', + 'When used a string as default single answer on singleSelect, the defaultValidator must return this answer as a string', + 'First response', + ], [ false, [0], diff --git a/src/Symfony/Component/CssSelector/LICENSE b/src/Symfony/Component/CssSelector/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/CssSelector/LICENSE +++ b/src/Symfony/Component/CssSelector/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index 73b79e4180bb5..a3f6cd7b3104d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -210,6 +210,10 @@ private function checkType(Definition $checkedDefinition, mixed $value, \Reflect $class = null; if ($value instanceof Definition) { + if ($value->getFactory()) { + return; + } + $class = $value->getClass(); if ($class && isset(self::BUILTIN_TYPES[strtolower($class)])) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index dc94a8b95fd73..2efb12b04894c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -126,7 +126,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed $this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file]; } - if (preg_match('/^(?:(?:array|bool|float|int|string|([^ $]++)) )\$/', $key, $m)) { + if (preg_match('/^(?:(?:array|bool|float|int|string|iterable|([^ $]++)) )\$/', $key, $m)) { $bindingNames[substr($key, \strlen($m[0]))] = $binding; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php index d403c0fb9af09..a1a14947727f1 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveChildDefinitionsPass.php @@ -115,6 +115,8 @@ private function doResolveDefinition(ChildDefinition $definition): Definition $def->setBindings($definition->getBindings() + $parentDef->getBindings()); + $def->setSynthetic($definition->isSynthetic()); + // overwrite with values specified in the decorator $changes = $definition->getChanges(); if (isset($changes['class'])) { diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 17650e4137b40..649ac2efe8052 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1341,7 +1341,7 @@ public function resolveEnvPlaceholders(mixed $value, string|bool $format = null, return $result; } - if (!\is_string($value) || 38 > \strlen($value)) { + if (!\is_string($value) || 38 > \strlen($value) || !preg_match('/env[_(]/i', $value)) { return $value; } $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index ca66af554044c..64991a4f42323 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -272,11 +272,17 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed } if ('resolve' === $prefix) { - return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name) { + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name, $getEnv) { if (!isset($match[1])) { return '%'; } - $value = $this->container->getParameter($match[1]); + + if (str_starts_with($match[1], 'env(') && str_ends_with($match[1], ')') && 'env()' !== $match[1]) { + $value = $getEnv(substr($match[1], 4, -1)); + } else { + $value = $this->container->getParameter($match[1]); + } + if (!is_scalar($value)) { throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, get_debug_type($value))); } diff --git a/src/Symfony/Component/DependencyInjection/LICENSE b/src/Symfony/Component/DependencyInjection/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/DependencyInjection/LICENSE +++ b/src/Symfony/Component/DependencyInjection/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php index b7fb0de443972..6bf6b6f4372b6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php @@ -12,10 +12,8 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; use Symfony\Component\DependencyInjection\Argument\BoundArgument; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Loader\Configurator\DefaultsConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\InstanceofConfigurator; -use Symfony\Component\DependencyInjection\Reference; trait BindTrait { @@ -34,9 +32,6 @@ trait BindTrait final public function bind(string $nameOrFqcn, mixed $valueOrRef): static { $valueOrRef = static::processValue($valueOrRef, true); - if (!preg_match('/^(?:(?:array|bool|float|int|string|iterable)[ \t]*+)?\$/', $nameOrFqcn) && !$valueOrRef instanceof Reference) { - throw new InvalidArgumentException(sprintf('Invalid binding for service "%s": named arguments must start with a "$", and FQCN must map to references. Neither applies to binding "%s".', $this->id, $nameOrFqcn)); - } $bindings = $this->definition->getBindings(); $type = $this instanceof DefaultsConfigurator ? BoundArgument::DEFAULTS_BINDING : ($this instanceof InstanceofConfigurator ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING); $bindings[$nameOrFqcn] = new BoundArgument($valueOrRef, true, $type, $this->path ?? null); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index d26c0928c1050..9b55c68e77885 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -972,6 +972,20 @@ public function testCallableClass() $this->addToAssertionCount(1); } + + public function testIgnoreDefinitionFactoryArgument() + { + $container = new ContainerBuilder(); + $container->register('bar', Bar::class) + ->setArguments([ + (new Definition(Foo::class)) + ->setFactory([Foo::class, 'createStdClass']), + ]); + + (new CheckTypeDeclarationsPass())->process($container); + + $this->addToAssertionCount(1); + } } class CallableClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index 7ca809b4df0cf..c12a49fefa535 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -28,6 +28,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedEnumArgumentDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedIterableArgumentDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists; use Symfony\Component\DependencyInjection\Tests\Fixtures\WithTarget; use Symfony\Component\DependencyInjection\TypedReference; @@ -212,6 +213,28 @@ public function testEmptyBindingTypehint() $pass->process($container); } + public function testIterableBindingTypehint() + { + $autoloader = static function ($class) { + if ('iterable' === $class) { + throw new \RuntimeException('We should not search pseudo-type iterable as class'); + } + }; + spl_autoload_register($autoloader); + + $container = new ContainerBuilder(); + $definition = $container->register('bar', NamedIterableArgumentDummy::class); + $definition->setBindings([ + 'iterable $items' => new TaggedIteratorArgument('foo'), + ]); + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $this->assertInstanceOf(TaggedIteratorArgument::class, $container->getDefinition('bar')->getArgument(0)); + + spl_autoload_unregister($autoloader); + } + public function testBindWithTarget() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php index b40c80032200e..4be6f803cd936 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php @@ -396,4 +396,21 @@ public function testProcessDetectsChildDefinitionIndirectCircularReference() $this->process($container); } + + public function testProcessCopiesSyntheticStatus() + { + $container = new ContainerBuilder(); + + $container->register('parent'); + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ->setSynthetic(true) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertTrue($def->isSynthetic()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php index b24dac61705f1..4441fe5422bc3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php @@ -506,6 +506,109 @@ public function testRequireFile() $this->assertEquals('foo', $result); } + /** + * @dataProvider validResolve + */ + public function testGetEnvResolve($value, $processed) + { + $container = new ContainerBuilder(); + $container->setParameter('bar', $value); + $container->compile(); + + $processor = new EnvVarProcessor($container); + + $result = $processor->getEnv('resolve', 'foo', function () { + return '%bar%'; + }); + + $this->assertSame($processed, $result); + } + + public function validResolve() + { + return [ + ['string', 'string'], + [1, '1'], + [1.1, '1.1'], + [true, '1'], + [false, ''], + ]; + } + + public function testGetEnvResolveNoMatch() + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('resolve', 'foo', function () { + return '%%'; + }); + + $this->assertSame('%', $result); + } + + /** + * @dataProvider notScalarResolve + */ + public function testGetEnvResolveNotScalar($value) + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Parameter "bar" found when resolving env var "foo" must be scalar'); + + $container = new ContainerBuilder(); + $container->setParameter('bar', $value); + $container->compile(); + + $processor = new EnvVarProcessor($container); + + $processor->getEnv('resolve', 'foo', function () { + return '%bar%'; + }); + } + + public function notScalarResolve() + { + return [ + [null], + [[]], + ]; + } + + public function testGetEnvResolveNestedEnv() + { + $container = new ContainerBuilder(); + $container->setParameter('env(BAR)', 'BAR in container'); + $container->compile(); + + $processor = new EnvVarProcessor($container); + $getEnv = \Closure::fromCallable([$processor, 'getEnv']); + + $result = $processor->getEnv('resolve', 'foo', function ($name) use ($getEnv) { + return 'foo' === $name ? '%env(BAR)%' : $getEnv('string', $name, function () {}); + }); + + $this->assertSame('BAR in container', $result); + } + + public function testGetEnvResolveNestedRealEnv() + { + $_ENV['BAR'] = 'BAR in environment'; + + $container = new ContainerBuilder(); + $container->setParameter('env(BAR)', 'BAR in container'); + $container->compile(); + + $processor = new EnvVarProcessor($container); + $getEnv = \Closure::fromCallable([$processor, 'getEnv']); + + $result = $processor->getEnv('resolve', 'foo', function ($name) use ($getEnv) { + return 'foo' === $name ? '%env(BAR)%' : $getEnv('string', $name, function () {}); + }); + + $this->assertSame('BAR in environment', $result); + + unset($_ENV['BAR']); + } + /** * @dataProvider validCsv */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php index be92ec5c297f4..e775def689305 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php @@ -23,4 +23,9 @@ public static function createArray(): array { return []; } + + public static function createStdClass(): \stdClass + { + return new \stdClass(); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedIterableArgumentDummy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedIterableArgumentDummy.php new file mode 100644 index 0000000000000..c2c9290df3473 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedIterableArgumentDummy.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Fixtures; + +class NamedIterableArgumentDummy +{ + public function __construct(iterable $items) + { + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php index 2ac1f3f02e95e..49545a8dfa2e6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php @@ -8,7 +8,7 @@ #[When(env: 'dev')] class Foo implements FooInterface, Sub\BarInterface { - public function __construct($bar = null, iterable $foo = null) + public function __construct($bar = null, iterable $foo = null, object $baz = null) { } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/inline_binding.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/inline_binding.expected.yml new file mode 100644 index 0000000000000..9c4ef3ca9d52a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/inline_binding.expected.yml @@ -0,0 +1,28 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + App\BarService: + class: App\BarService + public: true + arguments: [!service { class: FooClass }] + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + tags: + - t: { a: b } + autowire: true + autoconfigure: true + arguments: ['@bar', !tagged_iterator foo, !service { class: Baz }] + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + tags: + - t: { a: b } + autowire: true + arguments: [null, !tagged_iterator foo, !service { class: Baz }] + calls: + - [setFoo, ['@bar']] + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/inline_binding.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/inline_binding.php new file mode 100644 index 0000000000000..1542285cb3fb7 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/inline_binding.php @@ -0,0 +1,23 @@ +import('basic.php'); + + $s = $c->services()->defaults() + ->public() + ->private() + ->autoconfigure() + ->autowire() + ->tag('t', ['a' => 'b']) + ->bind(Foo::class, service('bar')) + ->bind('iterable $foo', tagged_iterator('foo')) + ->bind('object $baz', inline_service('Baz')) + ->public(); + + $s->set(Foo::class)->args([service('bar')])->public(); + $s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false); +}; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php index e62c2748956d1..8c5a7a7c03456 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -94,6 +94,7 @@ public function provideConfig() yield ['php7']; yield ['anonymous']; yield ['lazy_fqcn']; + yield ['inline_binding']; yield ['remove']; yield ['config_builder']; } diff --git a/src/Symfony/Component/DomCrawler/LICENSE b/src/Symfony/Component/DomCrawler/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/DomCrawler/LICENSE +++ b/src/Symfony/Component/DomCrawler/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 903111de81833..750ad5daa3f50 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -142,7 +142,7 @@ public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnv $env = is_file($p) ? include $p : null; $k = $this->envKey; - if (\is_array($env) && (!isset($env[$k]) || ($_SERVER[$k] ?? $_ENV[$k] ?? $env[$k]) === $env[$k])) { + if (\is_array($env) && ($overrideExistingVars || !isset($env[$k]) || ($_SERVER[$k] ?? $_ENV[$k] ?? $env[$k]) === $env[$k])) { $this->populate($env, $overrideExistingVars); } else { $this->loadEnv($path, $k, $defaultEnv, $testEnvs, $overrideExistingVars); diff --git a/src/Symfony/Component/Dotenv/LICENSE b/src/Symfony/Component/Dotenv/LICENSE index 3796612f43c2b..7fa9539054928 100644 --- a/src/Symfony/Component/Dotenv/LICENSE +++ b/src/Symfony/Component/Dotenv/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2021 Fabien Potencier +Copyright (c) 2016-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php index 4345792c0c7de..47c9bfba137ac 100644 --- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php +++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php @@ -587,6 +587,13 @@ public function testBootEnv() $this->assertSame('BAR', $_SERVER['FOO']); $this->assertSame('1', $_SERVER['TEST_APP_DEBUG']); $this->assertSame('localphpNEW_VALUE', $_SERVER['EXISTING_KEY']); + + $resetContext(); + $_SERVER['TEST_APP_ENV'] = 'ccc'; + (new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path, 'dev', ['test'], true); + $this->assertSame('BAR', $_SERVER['FOO']); + $this->assertSame('1', $_SERVER['TEST_APP_DEBUG']); + $this->assertSame('localphpNEW_VALUE', $_SERVER['EXISTING_KEY']); unlink($path.'.local.php'); $resetContext(); diff --git a/src/Symfony/Component/ErrorHandler/LICENSE b/src/Symfony/Component/ErrorHandler/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/ErrorHandler/LICENSE +++ b/src/Symfony/Component/ErrorHandler/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/EventDispatcher/LICENSE b/src/Symfony/Component/EventDispatcher/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/EventDispatcher/LICENSE +++ b/src/Symfony/Component/EventDispatcher/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/ExpressionLanguage/LICENSE b/src/Symfony/Component/ExpressionLanguage/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/ExpressionLanguage/LICENSE +++ b/src/Symfony/Component/ExpressionLanguage/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Filesystem/LICENSE b/src/Symfony/Component/Filesystem/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Filesystem/LICENSE +++ b/src/Symfony/Component/Filesystem/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Finder/Iterator/VcsIgnoredFilterIterator.php b/src/Symfony/Component/Finder/Iterator/VcsIgnoredFilterIterator.php index 2a84f4a1fa3ab..e27158cbd17a4 100644 --- a/src/Symfony/Component/Finder/Iterator/VcsIgnoredFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/VcsIgnoredFilterIterator.php @@ -60,8 +60,6 @@ private function isIgnored(string $fileRealPath): bool foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) { if ($this->isIgnored($parentDirectory)) { - $ignored = true; - // rules in ignored directories are ignored, no need to check further. break; } diff --git a/src/Symfony/Component/Finder/LICENSE b/src/Symfony/Component/Finder/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Finder/LICENSE +++ b/src/Symfony/Component/Finder/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Finder/Tests/Iterator/VcsIgnoredFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/VcsIgnoredFilterIteratorTest.php index 9a85c49ebaaa6..14cb3c4443ff4 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/VcsIgnoredFilterIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/VcsIgnoredFilterIteratorTest.php @@ -39,13 +39,17 @@ protected function tearDown(): void */ public function testAccept(array $gitIgnoreFiles, array $otherFileNames, array $expectedResult) { - foreach ($gitIgnoreFiles as $path => $content) { - $this->createFile("{$this->tmpDir}/{$path}", $content); - } - $otherFileNames = $this->toAbsolute($otherFileNames); foreach ($otherFileNames as $path) { - $this->createFile($path); + if (str_ends_with($path, '/')) { + mkdir($path); + } else { + touch($path); + } + } + + foreach ($gitIgnoreFiles as $path => $content) { + file_put_contents("{$this->tmpDir}/{$path}", $content); } $inner = new InnerNameIterator($otherFileNames); @@ -64,10 +68,12 @@ public function getAcceptData(): iterable [ 'a.txt', 'b.txt', + 'dir/', 'dir/a.txt', ], [ 'b.txt', + 'dir', ], ]; @@ -78,20 +84,23 @@ public function getAcceptData(): iterable [ 'a.txt', 'b.txt', + 'dir/', 'dir/a.txt', ], [ 'b.txt', + 'dir', 'dir/a.txt', ], ]; - yield 'directy' => [ + yield 'directory' => [ [ '.gitignore' => 'dir/', ], [ 'a.txt', + 'dir/', 'dir/a.txt', 'dir/b.txt', ], @@ -100,7 +109,7 @@ public function getAcceptData(): iterable ], ]; - yield 'directy matching a file' => [ + yield 'directory matching a file' => [ [ '.gitignore' => 'dir.txt/', ], @@ -112,15 +121,20 @@ public function getAcceptData(): iterable ], ]; - yield 'directy at root' => [ + yield 'directory at root' => [ [ '.gitignore' => '/dir/', ], [ + 'dir/', 'dir/a.txt', + 'other/', + 'other/dir/', 'other/dir/b.txt', ], [ + 'other', + 'other/dir', 'other/dir/b.txt', ], ]; @@ -131,11 +145,15 @@ public function getAcceptData(): iterable ], [ 'a.txt', + 'nested/', 'nested/a.txt', + 'nested/nested/', 'nested/nested/a.txt', ], [ 'a.txt', + 'nested', + 'nested/nested', ], ]; @@ -145,58 +163,81 @@ public function getAcceptData(): iterable ], [ 'a.txt', + 'nested/', 'nested/a.txt', + 'nested/nested/', 'nested/nested/a.txt', ], [ 'a.txt', + 'nested', + 'nested/nested', 'nested/nested/a.txt', ], ]; - yield 'directy in nested .gitignore' => [ + yield 'directory in nested .gitignore' => [ [ 'nested/.gitignore' => 'dir/', ], [ 'a.txt', + 'dir/', 'dir/a.txt', + 'nested/', + 'nested/dir/', 'nested/dir/a.txt', + 'nested/nested/', + 'nested/nested/dir/', 'nested/nested/dir/a.txt', ], [ 'a.txt', + 'dir', 'dir/a.txt', + 'nested', + 'nested/nested', ], ]; - yield 'directy matching a file in nested .gitignore' => [ + yield 'directory matching a file in nested .gitignore' => [ [ 'nested/.gitignore' => 'dir.txt/', ], [ 'dir.txt', + 'nested/', 'nested/dir.txt', ], [ 'dir.txt', + 'nested', 'nested/dir.txt', ], ]; - yield 'directy at root of nested .gitignore' => [ + yield 'directory at root of nested .gitignore' => [ [ 'nested/.gitignore' => '/dir/', ], [ 'a.txt', + 'dir/', 'dir/a.txt', + 'nested/', + 'nested/dir/', 'nested/dir/a.txt', + 'nested/nested/', + 'nested/nested/dir/', 'nested/nested/dir/a.txt', ], [ 'a.txt', + 'dir', 'dir/a.txt', + 'nested', + 'nested/nested', + 'nested/nested/dir', 'nested/nested/dir/a.txt', ], ]; @@ -209,12 +250,15 @@ public function getAcceptData(): iterable [ 'a.txt', 'b.txt', + 'nested/', 'nested/a.txt', 'nested/b.txt', + 'nested/dir/', 'nested/dir/a.txt', 'nested/dir/b.txt', ], [ + 'nested', 'nested/a.txt', ], ]; @@ -227,8 +271,10 @@ public function getAcceptData(): iterable [ 'a.txt', 'b.txt', + 'nested/', 'nested/a.txt', 'nested/b.txt', + 'nested/dir/', 'nested/dir/a.txt', 'nested/dir/b.txt', ], @@ -241,12 +287,33 @@ public function getAcceptData(): iterable 'a/.gitignore' => '!c/', ], [ + 'a/', + 'a/b/', + 'a/b/c/', 'a/b/c/d.txt', ], [ + 'a', + 'a/b', + 'a/b/c', 'a/b/c/d.txt', ], ]; + + yield 'file included from subdirectory with everything excluded' => [ + [ + '.gitignore' => "/a/**\n!/a/b.txt", + ], + [ + 'a/', + 'a/a.txt', + 'a/b.txt', + 'a/c.txt', + ], + [ + 'a/b.txt', + ], + ]; } public function testAcceptAtRootDirectory() @@ -267,20 +334,6 @@ private function toAbsolute(array $files): array return $files; } - private function createFile(string $path, string $content = null): void - { - $dir = \dirname($path); - if (!file_exists($dir)) { - mkdir($dir, 0777, true); - } - - if (null !== $content) { - file_put_contents($path, $content); - } else { - touch($path); - } - } - private function removeDirectory(string $dir): void { foreach ((new Finder())->in($dir)->ignoreDotFiles(false)->depth('< 1') as $file) { diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php index 825276fe165d0..d669395120e8a 100644 --- a/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php @@ -36,7 +36,7 @@ public function onSubmit(FormEvent $event) { $data = $event->getData(); - if ($this->defaultProtocol && $data && \is_string($data) && !preg_match('~^[\w+.-]+://~', $data)) { + if ($this->defaultProtocol && $data && \is_string($data) && !preg_match('~^([\w+.-]+://|[^:/?@#]++@)~', $data)) { $event->setData($this->defaultProtocol.'://'.$data); } } diff --git a/src/Symfony/Component/Form/LICENSE b/src/Symfony/Component/Form/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Form/LICENSE +++ b/src/Symfony/Component/Form/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php index e00cb9e9e1978..55fee6e2243ee 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php @@ -20,45 +20,49 @@ class FixUrlProtocolListenerTest extends TestCase { - public function testFixHttpUrl() + public function provideUrlToFix() { - $data = 'www.symfony.com'; - $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher())); - $event = new FormEvent($form, $data); - - $filter = new FixUrlProtocolListener('http'); - $filter->onSubmit($event); - - $this->assertEquals('http://www.symfony.com', $event->getData()); + return [ + ['www.symfony.com'], + ['twitter.com/@symfony'], + ['symfony.com?foo@bar'], + ['symfony.com#foo@bar'], + ['localhost'], + ]; } - public function testSkipKnownUrl() + /** + * @dataProvider provideUrlToFix + */ + public function testFixUrl($data) { - $data = 'http://www.symfony.com'; $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher())); $event = new FormEvent($form, $data); $filter = new FixUrlProtocolListener('http'); $filter->onSubmit($event); - $this->assertEquals('http://www.symfony.com', $event->getData()); + $this->assertEquals('http://'.$data, $event->getData()); } - public function provideUrlsWithSupportedProtocols() + public function provideUrlToSkip() { return [ + ['http://www.symfony.com'], ['ftp://www.symfony.com'], + ['https://twitter.com/@symfony'], ['chrome-extension://foo'], ['h323://foo'], ['iris.beep://foo'], ['foo+bar://foo'], + ['fabien@symfony.com'], ]; } /** - * @dataProvider provideUrlsWithSupportedProtocols + * @dataProvider provideUrlToSkip */ - public function testSkipOtherProtocol($url) + public function testSkipUrl($url) { $form = new Form(new FormConfigBuilder('name', null, new EventDispatcher())); $event = new FormEvent($form, $url); diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 23c832441bd5e..95d96978081c9 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -166,7 +166,6 @@ public function request(string $method, string $url, array $options = []): Respo if ($resolve && 0x072A00 > CurlClientState::$curlVersion['version_number']) { // DNS cache removals require curl 7.42 or higher - // On lower versions, we have to create a new multi handle $this->multi->reset(); } @@ -283,6 +282,7 @@ public function request(string $method, string $url, array $options = []): Respo if (!$pushedResponse) { $ch = curl_init(); $this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, $url)); + $curlopts += [\CURLOPT_SHARE => $this->multi->share]; } foreach ($curlopts as $opt => $value) { @@ -304,9 +304,9 @@ public function stream(ResponseInterface|iterable $responses, float $timeout = n $responses = [$responses]; } - if (($mh = $this->multi->handles[0] ?? null) instanceof \CurlMultiHandle) { + if ($this->multi->handle instanceof \CurlMultiHandle) { $active = 0; - while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($mh, $active)) { + while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)) { } } diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index cecb1c0d9220d..0f6168570495e 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -159,7 +159,10 @@ private static function prepareRequest(?string $method, ?string $url, array $opt // Finalize normalization of options $options['http_version'] = (string) ($options['http_version'] ?? '') ?: null; - $options['timeout'] = (float) ($options['timeout'] ?? ini_get('default_socket_timeout')); + if (0 > $options['timeout'] = (float) ($options['timeout'] ?? ini_get('default_socket_timeout'))) { + $options['timeout'] = 172800.0; // 2 days + } + $options['max_duration'] = isset($options['max_duration']) ? (float) $options['max_duration'] : 0; return [$url, $options]; diff --git a/src/Symfony/Component/HttpClient/HttplugClient.php b/src/Symfony/Component/HttpClient/HttplugClient.php index 2987710aadafb..5e399a38a62d9 100644 --- a/src/Symfony/Component/HttpClient/HttplugClient.php +++ b/src/Symfony/Component/HttpClient/HttplugClient.php @@ -13,6 +13,7 @@ use GuzzleHttp\Promise\Promise as GuzzlePromise; use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Promise\Utils; use Http\Client\Exception\NetworkException; use Http\Client\Exception\RequestException; use Http\Client\HttpAsyncClient; @@ -73,7 +74,7 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI { $this->client = $client ?? HttpClient::create(); $streamFactory ??= $responseFactory instanceof StreamFactoryInterface ? $responseFactory : null; - $this->promisePool = \function_exists('GuzzleHttp\Promise\queue') ? new \SplObjectStorage() : null; + $this->promisePool = class_exists(Utils::class) ? new \SplObjectStorage() : null; if (null === $responseFactory || null === $streamFactory) { if (!class_exists(Psr17Factory::class) && !class_exists(Psr17FactoryDiscovery::class)) { diff --git a/src/Symfony/Component/HttpClient/Internal/CurlClientState.php b/src/Symfony/Component/HttpClient/Internal/CurlClientState.php index 091671f0d0727..dc900d9beae58 100644 --- a/src/Symfony/Component/HttpClient/Internal/CurlClientState.php +++ b/src/Symfony/Component/HttpClient/Internal/CurlClientState.php @@ -23,11 +23,12 @@ */ final class CurlClientState extends ClientState { - /** @var array<\CurlMultiHandle> */ - public array $handles = []; + public ?\CurlMultiHandle $handle; + public ?\CurlShareHandle $share; + /** @var PushedResponse[] */ public array $pushedResponses = []; - public $dnsCache; + public DnsCache $dnsCache; /** @var float[] */ public array $pauseExpiries = []; public int $execCounter = \PHP_INT_MIN; @@ -35,27 +36,23 @@ final class CurlClientState extends ClientState public static array $curlVersion; - private int $maxHostConnections; - private int $maxPendingPushes; - public function __construct(int $maxHostConnections, int $maxPendingPushes) { self::$curlVersion = self::$curlVersion ?? curl_version(); - array_unshift($this->handles, $mh = curl_multi_init()); + $this->handle = curl_multi_init(); $this->dnsCache = new DnsCache(); - $this->maxHostConnections = $maxHostConnections; - $this->maxPendingPushes = $maxPendingPushes; + $this->reset(); // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order if (\defined('CURLPIPE_MULTIPLEX')) { - curl_multi_setopt($mh, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX); + curl_multi_setopt($this->handle, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX); } if (\defined('CURLMOPT_MAX_HOST_CONNECTIONS')) { - $maxHostConnections = curl_multi_setopt($mh, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections; + $maxHostConnections = curl_multi_setopt($this->handle, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections; } if (\defined('CURLMOPT_MAXCONNECTS') && 0 < $maxHostConnections) { - curl_multi_setopt($mh, \CURLMOPT_MAXCONNECTS, $maxHostConnections); + curl_multi_setopt($this->handle, \CURLMOPT_MAXCONNECTS, $maxHostConnections); } // Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535 @@ -70,14 +67,14 @@ public function __construct(int $maxHostConnections, int $maxPendingPushes) // Clone to prevent a circular reference $multi = clone $this; - $multi->handles = [$mh]; + $multi->handle = null; + $multi->share = null; $multi->pushedResponses = &$this->pushedResponses; $multi->logger = &$this->logger; $multi->handlesActivity = &$this->handlesActivity; $multi->openHandles = &$this->openHandles; - $multi->lastTimeout = &$this->lastTimeout; - curl_multi_setopt($mh, \CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) { + curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) { return $multi->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes); }); } @@ -86,10 +83,7 @@ public function reset() { foreach ($this->pushedResponses as $url => $response) { $this->logger && $this->logger->debug(sprintf('Unused pushed response: "%s"', $url)); - - foreach ($this->handles as $mh) { - curl_multi_remove_handle($mh, $response->handle); - } + curl_multi_remove_handle($this->handle, $response->handle); curl_close($response->handle); } @@ -97,11 +91,14 @@ public function reset() $this->dnsCache->evictions = $this->dnsCache->evictions ?: $this->dnsCache->removals; $this->dnsCache->removals = $this->dnsCache->hostnames = []; - if (\defined('CURLMOPT_PUSHFUNCTION')) { - curl_multi_setopt($this->handles[0], \CURLMOPT_PUSHFUNCTION, null); - } + $this->share = curl_share_init(); + + curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_DNS); + curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_SSL_SESSION); - $this->__construct($this->maxHostConnections, $this->maxPendingPushes); + if (\defined('CURL_LOCK_DATA_CONNECT')) { + curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_CONNECT); + } } private function handlePush($parent, $pushed, array $requestHeaders, int $maxPendingPushes): int diff --git a/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php b/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php index 10ff3d5ba8755..e3685c4d4d75d 100644 --- a/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php +++ b/src/Symfony/Component/HttpClient/Internal/HttplugWaitLoop.php @@ -52,7 +52,7 @@ public function wait(?ResponseInterface $pendingResponse, float $maxDuration = n return 0; } - $guzzleQueue = \GuzzleHttp\Promise\queue(); + $guzzleQueue = \GuzzleHttp\Promise\Utils::queue(); if (0.0 === $remainingDuration = $maxDuration) { $idleTimeout = 0.0; diff --git a/src/Symfony/Component/HttpClient/LICENSE b/src/Symfony/Component/HttpClient/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/HttpClient/LICENSE +++ b/src/Symfony/Component/HttpClient/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 935173272436f..23479326656f1 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -108,9 +108,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra if (0 < $duration) { if ($execCounter === $multi->execCounter) { $multi->execCounter = !\is_float($execCounter) ? 1 + $execCounter : \PHP_INT_MIN; - foreach ($multi->handles as $mh) { - curl_multi_remove_handle($mh, $ch); - } + curl_multi_remove_handle($multi->handle, $ch); } $lastExpiry = end($multi->pauseExpiries); @@ -122,7 +120,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra } else { unset($multi->pauseExpiries[(int) $ch]); curl_pause($ch, \CURLPAUSE_CONT); - curl_multi_add_handle($multi->handles[0], $ch); + curl_multi_add_handle($multi->handle, $ch); } }; @@ -176,7 +174,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra // Schedule the request in a non-blocking way $multi->lastTimeout = null; $multi->openHandles[$id] = [$ch, $options]; - curl_multi_add_handle($multi->handles[0], $ch); + curl_multi_add_handle($multi->handle, $ch); $this->canary = new Canary(static function () use ($ch, $multi, $id) { unset($multi->pauseExpiries[$id], $multi->openHandles[$id], $multi->handlesActivity[$id]); @@ -186,9 +184,7 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, arra return; } - foreach ($multi->handles as $mh) { - curl_multi_remove_handle($mh, $ch); - } + curl_multi_remove_handle($multi->handle, $ch); curl_setopt_array($ch, [ \CURLOPT_NOPROGRESS => true, \CURLOPT_PROGRESSFUNCTION => null, @@ -270,7 +266,7 @@ public function __destruct() */ private static function schedule(self $response, array &$runningResponses): void { - if (isset($runningResponses[$i = (int) $response->multi->handles[0]])) { + if (isset($runningResponses[$i = (int) $response->multi->handle])) { $runningResponses[$i][1][$response->id] = $response; } else { $runningResponses[$i] = [$response->multi, [$response->id => $response]]; @@ -303,47 +299,39 @@ private static function perform(ClientState $multi, array &$responses = null): v try { self::$performing = true; ++$multi->execCounter; + $active = 0; + while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($multi->handle, $active))) { + } - foreach ($multi->handles as $i => $mh) { - $active = 0; - while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($mh, $active))) { - } + if (\CURLM_OK !== $err) { + throw new TransportException(curl_multi_strerror($err)); + } - if (\CURLM_OK !== $err) { - throw new TransportException(curl_multi_strerror($err)); + while ($info = curl_multi_info_read($multi->handle)) { + if (\CURLMSG_DONE !== $info['msg']) { + continue; } + $result = $info['result']; + $id = (int) $ch = $info['handle']; + $waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0'; - while ($info = curl_multi_info_read($mh)) { - if (\CURLMSG_DONE !== $info['msg']) { - continue; - } - $result = $info['result']; - $id = (int) $ch = $info['handle']; - $waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0'; - - if (\in_array($result, [\CURLE_SEND_ERROR, \CURLE_RECV_ERROR, /*CURLE_HTTP2*/ 16, /*CURLE_HTTP2_STREAM*/ 92], true) && $waitFor[1] && 'C' !== $waitFor[0]) { - curl_multi_remove_handle($mh, $ch); - $waitFor[1] = (string) ((int) $waitFor[1] - 1); // decrement the retry counter - curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor); - curl_setopt($ch, \CURLOPT_FORBID_REUSE, true); - - if (0 === curl_multi_add_handle($mh, $ch)) { - continue; - } - } + if (\in_array($result, [\CURLE_SEND_ERROR, \CURLE_RECV_ERROR, /*CURLE_HTTP2*/ 16, /*CURLE_HTTP2_STREAM*/ 92], true) && $waitFor[1] && 'C' !== $waitFor[0]) { + curl_multi_remove_handle($multi->handle, $ch); + $waitFor[1] = (string) ((int) $waitFor[1] - 1); // decrement the retry counter + curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor); + curl_setopt($ch, \CURLOPT_FORBID_REUSE, true); - if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) { - $multi->handlesActivity[$id][] = new FirstChunk(); + if (0 === curl_multi_add_handle($multi->handle, $ch)) { + continue; } - - $multi->handlesActivity[$id][] = null; - $multi->handlesActivity[$id][] = \in_array($result, [\CURLE_OK, \CURLE_TOO_MANY_REDIRECTS], true) || '_0' === $waitFor || curl_getinfo($ch, \CURLINFO_SIZE_DOWNLOAD) === curl_getinfo($ch, \CURLINFO_CONTENT_LENGTH_DOWNLOAD) ? null : new TransportException(ucfirst(curl_error($ch) ?: curl_strerror($result)).sprintf(' for "%s".', curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL))); } - if (!$active && 0 < $i) { - curl_multi_close($mh); - unset($multi->handles[$i]); + if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) { + $multi->handlesActivity[$id][] = new FirstChunk(); } + + $multi->handlesActivity[$id][] = null; + $multi->handlesActivity[$id][] = \in_array($result, [\CURLE_OK, \CURLE_TOO_MANY_REDIRECTS], true) || '_0' === $waitFor || curl_getinfo($ch, \CURLINFO_SIZE_DOWNLOAD) === curl_getinfo($ch, \CURLINFO_CONTENT_LENGTH_DOWNLOAD) ? null : new TransportException(ucfirst(curl_error($ch) ?: curl_strerror($result)).sprintf(' for "%s".', curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL))); } } finally { self::$performing = false; @@ -368,11 +356,11 @@ private static function select(ClientState $multi, float $timeout): int unset($multi->pauseExpiries[$id]); curl_pause($multi->openHandles[$id][0], \CURLPAUSE_CONT); - curl_multi_add_handle($multi->handles[0], $multi->openHandles[$id][0]); + curl_multi_add_handle($multi->handle, $multi->openHandles[$id][0]); } } - if (0 !== $selected = curl_multi_select($multi->handles[array_key_last($multi->handles)], $timeout)) { + if (0 !== $selected = curl_multi_select($multi->handle, $timeout)) { return $selected; } diff --git a/src/Symfony/Component/HttpClient/Response/HttplugPromise.php b/src/Symfony/Component/HttpClient/Response/HttplugPromise.php index 66e0d90a863ff..09ed10df33b02 100644 --- a/src/Symfony/Component/HttpClient/Response/HttplugPromise.php +++ b/src/Symfony/Component/HttpClient/Response/HttplugPromise.php @@ -11,7 +11,7 @@ namespace Symfony\Component\HttpClient\Response; -use function GuzzleHttp\Promise\promise_for; +use GuzzleHttp\Promise\Create; use GuzzleHttp\Promise\PromiseInterface as GuzzlePromiseInterface; use Http\Promise\Promise as HttplugPromiseInterface; use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface; @@ -74,7 +74,7 @@ private function wrapThenCallback(?callable $callback): ?callable } return static function ($value) use ($callback) { - return promise_for($callback($value)); + return Create::promiseFor($callback($value)); }; } } diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php index b83ec30017c71..f1bc595479747 100644 --- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php @@ -62,9 +62,9 @@ public function testHandleIsReinitOnReset() $r = new \ReflectionProperty($httpClient, 'multi'); $r->setAccessible(true); $clientState = $r->getValue($httpClient); - $initialHandleId = (int) $clientState->handles[0]; + $initialShareId = $clientState->share; $httpClient->reset(); - self::assertNotSame($initialHandleId, (int) $clientState->handles[0]); + self::assertNotSame($initialShareId, $clientState->share); } public function testProcessAfterReset() diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 59e4dc1da7cc8..793053029164c 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -373,4 +373,13 @@ public function testDebugInfoOnDestruct() $this->assertNotEmpty($traceInfo['debug']); } + + public function testNegativeTimeout() + { + $client = $this->getHttpClient(__FUNCTION__); + + $this->assertSame(200, $client->request('GET', 'http://localhost:8057', [ + 'timeout' => -1, + ])->getStatusCode()); + } } diff --git a/src/Symfony/Component/HttpFoundation/LICENSE b/src/Symfony/Component/HttpFoundation/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/HttpFoundation/LICENSE +++ b/src/Symfony/Component/HttpFoundation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 6481da416278b..05f25959fd854 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -73,10 +73,12 @@ public function onKernelRequest(RequestEvent $event) } /* - * For supporting sessions in php runtime with runners like roadrunner or swoole the session - * cookie need read from the cookie bag and set on the session storage. + * For supporting sessions in php runtime with runners like roadrunner or swoole, the session + * cookie needs to be read from the cookie bag and set on the session storage. + * + * Do not set it when a native php session is active. */ - if ($sess && !$sess->isStarted()) { + if ($sess && !$sess->isStarted() && \PHP_SESSION_ACTIVE !== session_status()) { $sessionId = $request->cookies->get($sess->getName(), ''); $sess->setId($sessionId); } @@ -147,7 +149,8 @@ public function onKernelResponse(ResponseEvent $event) $request = $event->getRequest(); $requestSessionCookieId = $request->cookies->get($sessionName); - if ($requestSessionCookieId && ($session instanceof Session ? $session->isEmpty() : empty($session->all()))) { + $isSessionEmpty = ($session instanceof Session ? $session->isEmpty() : empty($session->all())) && empty($_SESSION); // checking $_SESSION to keep compatibility with native sessions + if ($requestSessionCookieId && $isSessionEmpty) { $response->headers->clearCookie( $sessionName, $sessionCookiePath, @@ -156,7 +159,7 @@ public function onKernelResponse(ResponseEvent $event) $sessionCookieHttpOnly, $sessionCookieSameSite ); - } elseif ($sessionId !== $requestSessionCookieId) { + } elseif ($sessionId !== $requestSessionCookieId && !$isSessionEmpty) { $expire = 0; $lifetime = $sessionOptions['cookie_lifetime'] ?? null; if ($lifetime) { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 3bae81558ce0e..41066986a17db 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 array $freshCache = []; - public const VERSION = '6.0.2'; - public const VERSION_ID = 60002; + public const VERSION = '6.0.3'; + public const VERSION_ID = 60003; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 0; - public const RELEASE_VERSION = 2; + public const RELEASE_VERSION = 3; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2022'; diff --git a/src/Symfony/Component/HttpKernel/LICENSE b/src/Symfony/Component/HttpKernel/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/HttpKernel/LICENSE +++ b/src/Symfony/Component/HttpKernel/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index 2da7169002ee5..1d4da8445149b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -22,6 +22,9 @@ use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\SessionFactory; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorageFactory; +use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorageFactory; +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageFactoryInterface; use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Event\ResponseEvent; @@ -124,6 +127,167 @@ public function provideSessionOptions(): \Generator ]; } + /** + * @runInSeparateProcess + */ + public function testPhpBridgeAlreadyStartedSession() + { + session_start(); + $sessionId = session_id(); + + $request = new Request(); + $listener = $this->createListener($request, new PhpBridgeSessionStorageFactory()); + + $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); + + $listener->onKernelRequest($event); + + $this->assertTrue($request->hasSession()); + $this->assertSame($sessionId, $request->getSession()->getId()); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieWrittenNoCookieGiven() + { + $request = new Request(); + $listener = $this->createListener($request, new NativeSessionStorageFactory()); + + $kernel = $this->createMock(HttpKernelInterface::class); + + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + $session = $request->getSession(); + $session->set('hello', 'world'); + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(1, $cookies); + $sessionCookie = $cookies[0]; + + $this->assertSame('PHPSESSID', $sessionCookie->getName()); + $this->assertNotEmpty($sessionCookie->getValue()); + $this->assertFalse($sessionCookie->isCleared()); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieNotWrittenCookieGiven() + { + $sessionId = $this->createValidSessionId(); + + $this->assertNotEmpty($sessionId); + + $request = new Request(); + $request->cookies->set('PHPSESSID', $sessionId); + + $listener = $this->createListener($request, new NativeSessionStorageFactory()); + + $kernel = $this->createMock(HttpKernelInterface::class); + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session = $request->getSession(); + $this->assertSame($sessionId, $session->getId()); + $session->set('hello', 'world'); + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + $this->assertSame($sessionId, $session->getId()); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieClearedWhenInvalidated() + { + $sessionId = $this->createValidSessionId(); + $request = new Request(); + $request->cookies->set('PHPSESSID', $sessionId); + $listener = $this->createListener($request, new NativeSessionStorageFactory()); + $kernel = $this->createMock(HttpKernelInterface::class); + + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session = $request->getSession(); + $session->start(); + $sessionId = $session->getId(); + $this->assertNotEmpty($sessionId); + $_SESSION['hello'] = 'world'; // check compatibility to php session bridge + + $session->invalidate(); + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(1, $cookies); + $sessionCookie = $cookies[0]; + + $this->assertSame('PHPSESSID', $sessionCookie->getName()); + $this->assertTrue($sessionCookie->isCleared()); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieNotClearedWhenOtherVariablesSet() + { + $sessionId = $this->createValidSessionId(); + $request = new Request(); + $request->cookies->set('PHPSESSID', $sessionId); + $listener = $this->createListener($request, new NativeSessionStorageFactory()); + $kernel = $this->createMock(HttpKernelInterface::class); + + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session = $request->getSession(); + $session->start(); + $sessionId = $session->getId(); + $this->assertNotEmpty($sessionId); + $_SESSION['hello'] = 'world'; + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(0, $cookies); + } + + /** + * @runInSeparateProcess + */ + public function testSessionCookieSetWhenOtherNativeVariablesSet() + { + $request = new Request(); + $listener = $this->createListener($request, new NativeSessionStorageFactory()); + $kernel = $this->createMock(HttpKernelInterface::class); + + $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST)); + + $session = $request->getSession(); + $session->start(); + $sessionId = $session->getId(); + $this->assertNotEmpty($sessionId); + $_SESSION['hello'] = 'world'; + + $response = new Response(); + $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response)); + + $cookies = $response->headers->getCookies(); + $this->assertCount(1, $cookies); + $sessionCookie = $cookies[0]; + + $this->assertSame('PHPSESSID', $sessionCookie->getName()); + $this->assertNotEmpty($sessionCookie->getValue()); + $this->assertFalse($sessionCookie->isCleared()); + } + public function testOnlyTriggeredOnMainRequest() { $listener = $this->getMockForAbstractClass(AbstractSessionListener::class); @@ -599,4 +763,33 @@ public function testResetUnclosedSession() $this->assertEmpty(session_id()); $this->assertSame(\PHP_SESSION_NONE, session_status()); } + + private function createListener(Request $request, SessionStorageFactoryInterface $sessionFactory) + { + $requestStack = new RequestStack(); + $request = new Request(); + $requestStack->push($request); + + $sessionFactory = new SessionFactory($requestStack, $sessionFactory); + + $container = new Container(); + $container->set('request_stack', $requestStack); + $container->set('session_factory', $sessionFactory); + + $listener = new SessionListener($container); + + return new SessionListener($container); + } + + private function createValidSessionId(): string + { + session_start(); + $sessionId = session_id(); + $_SESSION['some'] = 'value'; + session_write_close(); + $_SESSION = []; + session_abort(); + + return $sessionId; + } } diff --git a/src/Symfony/Component/Intl/LICENSE b/src/Symfony/Component/Intl/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Intl/LICENSE +++ b/src/Symfony/Component/Intl/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Ldap/LICENSE b/src/Symfony/Component/Ldap/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Ldap/LICENSE +++ b/src/Symfony/Component/Ldap/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Lock/LICENSE b/src/Symfony/Component/Lock/LICENSE index 3796612f43c2b..7fa9539054928 100644 --- a/src/Symfony/Component/Lock/LICENSE +++ b/src/Symfony/Component/Lock/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2021 Fabien Potencier +Copyright (c) 2016-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php index 4db2d2c614b38..f44c2fd5cfc6e 100644 --- a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php @@ -99,7 +99,7 @@ public function provideDsn() public function testCreatesTableInTransaction(string $platform) { $conn = $this->createMock(Connection::class); - $conn->expects($this->exactly(3)) + $conn->expects($this->atLeast(3)) ->method('executeStatement') ->withConsecutive( [$this->stringContains('INSERT INTO')], @@ -145,7 +145,7 @@ public function providePlatforms() public function testTableCreationInTransactionNotSupported() { $conn = $this->createMock(Connection::class); - $conn->expects($this->exactly(2)) + $conn->expects($this->atLeast(2)) ->method('executeStatement') ->withConsecutive( [$this->stringContains('INSERT INTO')], @@ -168,7 +168,7 @@ public function testTableCreationInTransactionNotSupported() $platform->method('getCreateTableSQL') ->willReturn(['create sql stmt']); - $conn->expects($this->exactly(2)) + $conn->expects($this->atLeast(2)) ->method('getDatabasePlatform'); $store = new DoctrineDbalStore($conn); @@ -181,7 +181,7 @@ public function testTableCreationInTransactionNotSupported() public function testCreatesTableOutsideTransaction() { $conn = $this->createMock(Connection::class); - $conn->expects($this->exactly(3)) + $conn->expects($this->atLeast(3)) ->method('executeStatement') ->withConsecutive( [$this->stringContains('INSERT INTO')], diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE b/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Google/LICENSE b/src/Symfony/Component/Mailer/Bridge/Google/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Google/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Google/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE b/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE b/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE b/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE b/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE b/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE b/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE b/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/LICENSE b/src/Symfony/Component/Mailer/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Mailer/LICENSE +++ b/src/Symfony/Component/Mailer/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE b/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE b/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE b/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE b/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index f97e2c1c49835..08b8d0e8f6368 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -189,7 +189,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->eventDispatcher->addSubscriber(new StopWorkerOnMemoryLimitListener($this->convertToBytes($memoryLimit), $this->logger)); } - if ($timeLimit = $input->getOption('time-limit')) { + if (null !== ($timeLimit = $input->getOption('time-limit'))) { $stopsWhen[] = "been running for {$timeLimit}s"; $this->eventDispatcher->addSubscriber(new StopWorkerOnTimeLimitListener($timeLimit, $this->logger)); } diff --git a/src/Symfony/Component/Messenger/LICENSE b/src/Symfony/Component/Messenger/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/Messenger/LICENSE +++ b/src/Symfony/Component/Messenger/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mime/LICENSE b/src/Symfony/Component/Mime/LICENSE index 151af4bbc71b9..298be14166c20 100644 --- a/src/Symfony/Component/Mime/LICENSE +++ b/src/Symfony/Component/Mime/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2021 Fabien Potencier +Copyright (c) 2010-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE b/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE b/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE b/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE b/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE b/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE b/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE b/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE b/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FreeMobile/FreeMobileTransport.php b/src/Symfony/Component/Notifier/Bridge/FreeMobile/FreeMobileTransport.php index 9f70676eebada..56ebe42bb8dfe 100644 --- a/src/Symfony/Component/Notifier/Bridge/FreeMobile/FreeMobileTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/FreeMobile/FreeMobileTransport.php @@ -60,7 +60,7 @@ protected function doSend(MessageInterface $message): SentMessage $endpoint = sprintf('https://%s', $this->getEndpoint()); $response = $this->client->request('POST', $endpoint, [ - 'json' => [ + 'query' => [ 'user' => $this->login, 'pass' => $this->password, 'msg' => $message->getSubject(), diff --git a/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE b/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE b/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE b/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE b/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE b/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE b/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE b/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE b/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE b/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE b/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE b/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE b/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE b/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE b/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE b/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php b/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php index 86abb718d5609..9643b0e53a149 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php @@ -64,6 +64,7 @@ protected function doSend(MessageInterface $message): SentMessage 'to' => $message->getPhone(), 'message' => $message->getSubject(), 'format' => 'json', + 'encoding' => 'utf-8', ], ]); diff --git a/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE b/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE b/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE b/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE b/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE b/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE b/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE b/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE b/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/LICENSE b/src/Symfony/Component/Notifier/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/LICENSE +++ b/src/Symfony/Component/Notifier/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/OptionsResolver/LICENSE b/src/Symfony/Component/OptionsResolver/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/OptionsResolver/LICENSE +++ b/src/Symfony/Component/OptionsResolver/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/PasswordHasher/LICENSE b/src/Symfony/Component/PasswordHasher/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/PasswordHasher/LICENSE +++ b/src/Symfony/Component/PasswordHasher/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Process/LICENSE b/src/Symfony/Component/Process/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Process/LICENSE +++ b/src/Symfony/Component/Process/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Process/Pipes/AbstractPipes.php b/src/Symfony/Component/Process/Pipes/AbstractPipes.php index c2014a6829c0f..0e7e5b67161fd 100644 --- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php +++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php @@ -47,7 +47,9 @@ public function __construct(mixed $input) public function close() { foreach ($this->pipes as $pipe) { - fclose($pipe); + if (\is_resource($pipe)) { + fclose($pipe); + } } $this->pipes = []; } diff --git a/src/Symfony/Component/PropertyAccess/LICENSE b/src/Symfony/Component/PropertyAccess/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/PropertyAccess/LICENSE +++ b/src/Symfony/Component/PropertyAccess/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 70f324ec908af..510cfa7516671 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -427,11 +427,11 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid } } catch (\Error $e) { // handle uninitialized properties in PHP >= 7.4 - if (preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { - $r = new \ReflectionProperty($matches[1], $matches[2]); + if (preg_match('/^Typed property ('.preg_quote(get_debug_type($object), '/').')::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { + $r = new \ReflectionProperty($class, $matches[2]); $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type; - throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type), 0, $e); + throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e); } throw $e; diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 3db072d1a1cf6..cfffdb2f71492 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\ArrayAdapter; -use Symfony\Component\PropertyAccess\Exception\AccessException; use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; @@ -144,44 +143,73 @@ public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter() public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass() { - $this->expectException(AccessException::class); + $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "class@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); - $object = eval('return new class() { + $object = new class() { private $uninitialized; public function getUninitialized(): array { return $this->uninitialized; } - };'); + }; + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + public function testGetValueThrowsExceptionIfUninitializedNotNullablePropertyWithGetterOfAnonymousClass() + { + $this->expectException(UninitializedPropertyException::class); + $this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.'); + + $object = new class() { + private string $uninitialized; + + public function getUninitialized(): string + { + return $this->uninitialized; + } + }; + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + public function testGetValueThrowsExceptionIfUninitializedPropertyOfAnonymousClass() + { + $this->expectException(UninitializedPropertyException::class); + $this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.'); + + $object = new class() { + public string $uninitialized; + }; $this->propertyAccessor->getValue($object, 'uninitialized'); } public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass() { - $this->expectException(AccessException::class); + $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "stdClass@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); - $object = eval('return new class() extends \stdClass { + $object = new class() extends \stdClass { private $uninitialized; public function getUninitialized(): array { return $this->uninitialized; } - };'); + }; $this->propertyAccessor->getValue($object, 'uninitialized'); } public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass() { - $this->expectException(AccessException::class); + $this->expectException(UninitializedPropertyException::class); $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); - $object = eval('return new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};'); + $object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {}; $this->propertyAccessor->getValue($object, 'uninitialized'); } @@ -684,7 +712,7 @@ public function testAnonymousClassWrite() private function generateAnonymousClass($value) { - $obj = eval('return new class($value) + return new class($value) { private $foo; @@ -708,9 +736,7 @@ public function setFoo($foo) { $this->foo = $foo; } - };'); - - return $obj; + }; } public function testThrowTypeErrorInsideSetterCall() diff --git a/src/Symfony/Component/PropertyInfo/LICENSE b/src/Symfony/Component/PropertyInfo/LICENSE index c9f0202b242b6..4e90b1b5ae4df 100644 --- a/src/Symfony/Component/PropertyInfo/LICENSE +++ b/src/Symfony/Component/PropertyInfo/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2021 Fabien Potencier +Copyright (c) 2015-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/RateLimiter/LICENSE b/src/Symfony/Component/RateLimiter/LICENSE index 3796612f43c2b..7fa9539054928 100644 --- a/src/Symfony/Component/RateLimiter/LICENSE +++ b/src/Symfony/Component/RateLimiter/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2021 Fabien Potencier +Copyright (c) 2016-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/RateLimiter/Policy/SlidingWindow.php b/src/Symfony/Component/RateLimiter/Policy/SlidingWindow.php index 3ec5e9f0f84a6..a498723408a11 100644 --- a/src/Symfony/Component/RateLimiter/Policy/SlidingWindow.php +++ b/src/Symfony/Component/RateLimiter/Policy/SlidingWindow.php @@ -102,6 +102,6 @@ public function getHitCount(): int public function getRetryAfter(): \DateTimeImmutable { - return \DateTimeImmutable::createFromFormat('U.u', $this->windowEndAt); + return \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6F', $this->windowEndAt)); } } diff --git a/src/Symfony/Component/RateLimiter/RateLimit.php b/src/Symfony/Component/RateLimiter/RateLimit.php index aa21d28f8ae79..fc693a2858c80 100644 --- a/src/Symfony/Component/RateLimiter/RateLimit.php +++ b/src/Symfony/Component/RateLimiter/RateLimit.php @@ -72,6 +72,6 @@ public function wait(): void return; } - usleep($delta * 1e6); + usleep((int) ($delta * 1e6)); } } diff --git a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowTest.php index f81784706c185..df1d01499679b 100644 --- a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowTest.php +++ b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowTest.php @@ -102,4 +102,13 @@ public function testGetRetryAfterUsesMicrotime() // should be 500ms left (10 - 9.5) $this->assertEqualsWithDelta(0.5, $window->getRetryAfter()->format('U.u') - microtime(true), 0.2); } + + public function testCreateAtExactTime() + { + ClockMock::register(SlidingWindow::class); + ClockMock::withClockMock(1234567890.000000); + $window = new SlidingWindow('foo', 10); + $window->getRetryAfter(); + $this->assertEquals('1234567900.000000', $window->getRetryAfter()->format('U.u')); + } } diff --git a/src/Symfony/Component/Routing/LICENSE b/src/Symfony/Component/Routing/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Routing/LICENSE +++ b/src/Symfony/Component/Routing/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Runtime/LICENSE b/src/Symfony/Component/Runtime/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Runtime/LICENSE +++ b/src/Symfony/Component/Runtime/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Runtime/SymfonyRuntime.php b/src/Symfony/Component/Runtime/SymfonyRuntime.php index 4a8782b55d035..8e1cc394744c3 100644 --- a/src/Symfony/Component/Runtime/SymfonyRuntime.php +++ b/src/Symfony/Component/Runtime/SymfonyRuntime.php @@ -98,13 +98,24 @@ public function __construct(array $options = []) } elseif (isset($_SERVER['argv']) && class_exists(ArgvInput::class)) { $this->options = $options; $this->getInput(); + $inputEnv = $_SERVER[$envKey] ?? null; + $inputDebug = $_SERVER[$debugKey] ?? null; } if (!($options['disable_dotenv'] ?? false) && isset($options['project_dir']) && !class_exists(MissingDotenv::class, false)) { (new Dotenv($envKey, $debugKey)) ->setProdEnvs((array) ($options['prod_envs'] ?? ['prod'])) ->usePutenv($options['use_putenv'] ?? false) - ->bootEnv($options['project_dir'].'/'.($options['dotenv_path'] ?? '.env'), 'dev', (array) ($options['test_envs'] ?? ['test']), $options['dotenv_overload'] ?? false); + ->bootEnv($options['project_dir'].'/'.($options['dotenv_path'] ?? '.env'), 'dev', (array) ($options['test_envs'] ?? ['test']), $dotenvOverload = $options['dotenv_overload'] ?? false); + if ($dotenvOverload) { + if (isset($inputEnv) && $inputEnv !== $_SERVER[$envKey]) { + throw new \LogicException(sprintf('Cannot use "--env" or "-e" when the "%s" file defines "%s" and the "dotenv_overload" runtime option is true.', $options['dotenv_path'] ?? '.env', $envKey)); + } + + if (isset($inputDebug) && $inputDebug !== $_SERVER[$debugKey]) { + putenv($debugKey.'='.$_SERVER[$debugKey] = $_ENV[$debugKey] = $inputDebug); + } + } $options['debug'] ?? $options['debug'] = '1' === $_SERVER[$debugKey]; $options['disable_dotenv'] = true; } else { diff --git a/src/Symfony/Component/Runtime/Tests/phpt/.env b/src/Symfony/Component/Runtime/Tests/phpt/.env index 9fd6ab5426972..1d124dd203a81 100644 --- a/src/Symfony/Component/Runtime/Tests/phpt/.env +++ b/src/Symfony/Component/Runtime/Tests/phpt/.env @@ -1,3 +1,4 @@ SOME_VAR=foo_bar ENV_MODE=foo DEBUG_MODE=0 +DEBUG_ENABLED=1 diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_conflict.php b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_conflict.php new file mode 100644 index 0000000000000..d730e08dbf734 --- /dev/null +++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_conflict.php @@ -0,0 +1,10 @@ + 'ENV_MODE', + 'dotenv_overload' => true, +]; + +require __DIR__.'/autoload.php'; + +return static function (): void {}; diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_conflict.phpt b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_conflict.phpt new file mode 100644 index 0000000000000..a63acb22de295 --- /dev/null +++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_conflict.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a command --env option conflicts with the different one defined in .env when the dotenv_overload option is true +--INI-- +display_errors=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught LogicException: Cannot use "--env" or "-e" when the ".env" file defines "ENV_MODE" and the "dotenv_overload" runtime option is true.%a diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php new file mode 100644 index 0000000000000..fadbabb4fd871 --- /dev/null +++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php @@ -0,0 +1,17 @@ + 'DEBUG_ENABLED', + 'dotenv_overload' => true, +]; + +require __DIR__.'/autoload.php'; + +return static function (Command $command, OutputInterface $output, array $context): Command { + return $command->setCode(static function () use ($output, $context): void { + $output->writeln($context['DEBUG_ENABLED']); + }); +}; diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.phpt b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.phpt new file mode 100644 index 0000000000000..4c6ae429901e4 --- /dev/null +++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a command --no-debug option has a higher priority than the debug value defined in .env when the dotenv_overload option is true +--INI-- +display_errors=1 +--FILE-- + +--EXPECTF-- +0 diff --git a/src/Symfony/Component/Security/Core/LICENSE b/src/Symfony/Component/Security/Core/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Security/Core/LICENSE +++ b/src/Symfony/Component/Security/Core/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Security/Csrf/LICENSE b/src/Symfony/Component/Security/Csrf/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Security/Csrf/LICENSE +++ b/src/Symfony/Component/Security/Csrf/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Security/Http/LICENSE b/src/Symfony/Component/Security/Http/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Security/Http/LICENSE +++ b/src/Symfony/Component/Security/Http/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Semaphore/LICENSE b/src/Symfony/Component/Semaphore/LICENSE index 3796612f43c2b..7fa9539054928 100644 --- a/src/Symfony/Component/Semaphore/LICENSE +++ b/src/Symfony/Component/Semaphore/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2021 Fabien Potencier +Copyright (c) 2016-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Serializer/LICENSE b/src/Symfony/Component/Serializer/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Serializer/LICENSE +++ b/src/Symfony/Component/Serializer/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index b0a1f3218b830..7f86ed8d786e4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -296,7 +296,7 @@ protected function isAllowedAttribute(object|string $classOrObject, string $attr * Normalizes the given data to an array. It's particularly useful during * the denormalization process. */ - protected function prepareForDenormalization(object|array|null $data): array + protected function prepareForDenormalization(mixed $data): array { return (array) $data; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index a241215133532..5b9a40b37bbf7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -570,6 +570,10 @@ private function validateAndDenormalize(array $types, string $currentClass, stri return (float) $data; } + if (Type::BUILTIN_TYPE_FALSE === $builtinType && false === $data) { + return $data; + } + if (('is_'.$builtinType)($data)) { return $data; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index b93671aaf9c44..03e5a889ff06d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -14,6 +14,7 @@ use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; +use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Exception\ExtraAttributesException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; @@ -387,6 +388,17 @@ public function testNormalizeEmptyObject() $normalizedData = $normalizer->normalize(new EmptyDummy(), 'any', ['preserve_empty_objects' => true]); $this->assertEquals(new \ArrayObject(), $normalizedData); } + + public function testDenormalizeRecursiveWithObjectAttributeWithStringValue() + { + $extractor = new ReflectionExtractor(); + $normalizer = new ObjectNormalizer(null, null, null, $extractor); + $serializer = new Serializer([$normalizer]); + + $obj = $serializer->denormalize(['inner' => 'foo'], ObjectOuter::class); + + $this->assertInstanceOf(ObjectInner::class, $obj->getInner()); + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index f7aba27286233..2384436c49400 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -56,6 +56,7 @@ use Symfony\Component\Serializer\Tests\Normalizer\Features\TypedPropertiesObject; use Symfony\Component\Serializer\Tests\Normalizer\Features\TypedPropertiesObjectWithGetters; use Symfony\Component\Serializer\Tests\Normalizer\Features\TypeEnforcementTestTrait; +use Symfony\Component\Serializer\Tests\Php80Dummy; /** * @author Kévin Dunglas @@ -755,6 +756,22 @@ public function testExtractAttributesRespectsContext() $this->assertSame(['foo' => 'bar', 'bar' => 'foo'], $normalizer->normalize($data, null, ['include_foo_and_bar' => true])); } + public function testDenormalizeFalsePseudoType() + { + // given a serializer that extracts the attribute types of an object via ReflectionExtractor + $propertyTypeExtractor = new PropertyInfoExtractor([], [new ReflectionExtractor()], [], [], []); + $objectNormalizer = new ObjectNormalizer(null, null, null, $propertyTypeExtractor); + + $serializer = new Serializer([$objectNormalizer]); + + // when denormalizing some data into an object where an attribute uses the false pseudo type + /** @var Php80Dummy $object */ + $object = $serializer->denormalize(['canBeFalseOrString' => false], Php80Dummy::class); + + // then the attribute that declared false was filled correctly + $this->assertFalse($object->canBeFalseOrString); + } + public function testAdvancedNameConverter() { $nameConverter = new class() implements AdvancedNameConverterInterface { diff --git a/src/Symfony/Component/Serializer/Tests/Php80Dummy.php b/src/Symfony/Component/Serializer/Tests/Php80Dummy.php new file mode 100644 index 0000000000000..baa75b1246659 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Php80Dummy.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests; + +final class Php80Dummy +{ + public false|string $canBeFalseOrString; +} diff --git a/src/Symfony/Component/Stopwatch/LICENSE b/src/Symfony/Component/Stopwatch/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Stopwatch/LICENSE +++ b/src/Symfony/Component/Stopwatch/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/String/LICENSE b/src/Symfony/Component/String/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/String/LICENSE +++ b/src/Symfony/Component/String/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2021 Fabien Potencier +Copyright (c) 2019-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Templating/LICENSE b/src/Symfony/Component/Templating/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Templating/LICENSE +++ b/src/Symfony/Component/Templating/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE b/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE +++ b/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Bridge/Loco/LICENSE b/src/Symfony/Component/Translation/Bridge/Loco/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/LICENSE +++ b/src/Symfony/Component/Translation/Bridge/Loco/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php b/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php index ec88efe8fe15c..df1e757368965 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php +++ b/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php @@ -79,9 +79,14 @@ public function write(TranslatorBagInterface $translatorBag): void $keysIdsMap[$this->retrieveKeyFromId($id, $domain)] = $id; } - $ids = array_intersect_key($keysIdsMap, $messages); + $assets = []; + foreach ($keysIdsMap as $key => $id) { + if (isset($messages[$key])) { + $assets[$id] = $messages[$key]; + } + } - $this->translateAssets(array_combine(array_values($ids), array_values($messages)), $locale); + $this->translateAssets($assets, $locale); } } } diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE b/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE index efb17f98e7dd3..48d17c4fb34f1 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021 Fabien Potencier +Copyright (c) 2021-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Command/TranslationPullCommand.php b/src/Symfony/Component/Translation/Command/TranslationPullCommand.php index 7d9da1c0e837a..42513a8a8742b 100644 --- a/src/Symfony/Component/Translation/Command/TranslationPullCommand.php +++ b/src/Symfony/Component/Translation/Command/TranslationPullCommand.php @@ -110,7 +110,7 @@ protected function configure() Full example: - php %command.full_name% provider --force --domains=messages,validators --locales=en + php %command.full_name% provider --force --domains=messages --domains=validators --locales=en This command pulls all translations associated with the messages and validators domains for the en locale. Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case. diff --git a/src/Symfony/Component/Translation/Command/TranslationPushCommand.php b/src/Symfony/Component/Translation/Command/TranslationPushCommand.php index 9654ec6a48e28..dba21643da669 100644 --- a/src/Symfony/Component/Translation/Command/TranslationPushCommand.php +++ b/src/Symfony/Component/Translation/Command/TranslationPushCommand.php @@ -102,7 +102,7 @@ protected function configure() Full example: - php %command.full_name% provider --force --delete-missing --domains=messages,validators --locales=en + php %command.full_name% provider --force --delete-missing --domains=messages --domains=validators --locales=en This command pushes all translations associated with the messages and validators domains for the en locale. Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case. diff --git a/src/Symfony/Component/Translation/LICENSE b/src/Symfony/Component/Translation/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Translation/LICENSE +++ b/src/Symfony/Component/Translation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Uid/LICENSE b/src/Symfony/Component/Uid/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Component/Uid/LICENSE +++ b/src/Symfony/Component/Uid/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index d6dcdf178f421..46432f2f4c60a 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -224,6 +224,10 @@ public function __isset(string $option): bool */ public function addImplicitGroupName(string $group) { + if (null === $this->groups && \array_key_exists('groups', (array) $this)) { + throw new \LogicException(sprintf('"%s::$groups" is set to null. Did you forget to call "%s::__construct()"?', static::class, self::class)); + } + if (\in_array(self::DEFAULT_GROUP, $this->groups) && !\in_array($group, $this->groups)) { $this->groups[] = $group; } diff --git a/src/Symfony/Component/Validator/Constraints/Choice.php b/src/Symfony/Component/Validator/Constraints/Choice.php index f0a657136b896..31691d4b90c14 100644 --- a/src/Symfony/Component/Validator/Constraints/Choice.php +++ b/src/Symfony/Component/Validator/Constraints/Choice.php @@ -52,7 +52,8 @@ public function getDefaultOption(): ?string } public function __construct( - string|array $choices = null, + string|array $options = [], + array $choices = null, callable|string $callback = null, bool $multiple = null, bool $strict = null, @@ -63,12 +64,13 @@ public function __construct( string $minMessage = null, string $maxMessage = null, array $groups = null, - mixed $payload = null, - array $options = [] + mixed $payload = null ) { - if (\is_array($choices) && \is_string(key($choices))) { - $options = array_merge($choices, $options); - } elseif (null !== $choices) { + if (\is_array($options) && $options && array_is_list($options)) { + $choices ??= $options; + $options = []; + } + if (null !== $choices) { $options['value'] = $choices; } diff --git a/src/Symfony/Component/Validator/Constraints/CssColor.php b/src/Symfony/Component/Validator/Constraints/CssColor.php index 19fcd000de228..e1510dafe38f2 100644 --- a/src/Symfony/Component/Validator/Constraints/CssColor.php +++ b/src/Symfony/Component/Validator/Constraints/CssColor.php @@ -72,7 +72,7 @@ public function __construct($formats = [], string $message = null, array $groups if (!$formats) { $options['value'] = self::$validationModes; } elseif (\is_array($formats) && \is_string(key($formats))) { - $options = array_merge($formats, $options); + $options = array_merge($formats, $options ?? []); } elseif (\is_array($formats)) { if ([] === array_intersect(self::$validationModes, $formats)) { throw new InvalidArgumentException(sprintf('The "formats" parameter value is not valid. It must contain one or more of the following values: "%s".', $validationModesAsString)); diff --git a/src/Symfony/Component/Validator/LICENSE b/src/Symfony/Component/Validator/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Validator/LICENSE +++ b/src/Symfony/Component/Validator/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf index 247ccf24021a7..55a811134dae5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf @@ -394,6 +394,14 @@ This value is not a valid CSS color. Táto hodnota nie je platná CSS farba. + + This value is not a valid CIDR notation. + Táto hodnota nie je platnou notáciou CIDR. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Hodnota masky siete by mala byť medzi {{ min }} a {{ max }}. + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php index 8b5879aedcb42..cbdaf3f8e8aa4 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php @@ -12,6 +12,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraints\Choice; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; use Symfony\Component\Validator\Tests\Fixtures\ConstraintChoiceWithPreset; class ChoiceTest extends TestCase @@ -22,4 +25,47 @@ public function testSetDefaultPropertyChoice() self::assertEquals(['A', 'B', 'C'], $constraint->choices); } + + public function testAttributes() + { + $metadata = new ClassMetadata(ChoiceDummy::class); + $loader = new AnnotationLoader(); + self::assertTrue($loader->loadClassMetadata($metadata)); + + /** @var Choice $aConstraint */ + [$aConstraint] = $metadata->properties['a']->getConstraints(); + self::assertSame([1, 2], $aConstraint->choices); + self::assertSame(['Default', 'ChoiceDummy'], $aConstraint->groups); + + /** @var Choice $bConstraint */ + [$bConstraint] = $metadata->properties['b']->getConstraints(); + self::assertSame(['foo', 'bar'], $bConstraint->choices); + self::assertSame('myMessage', $bConstraint->message); + self::assertSame(['Default', 'ChoiceDummy'], $bConstraint->groups); + + /** @var Choice $cConstraint */ + [$cConstraint] = $metadata->properties['c']->getConstraints(); + self::assertSame([1, 2], $aConstraint->choices); + self::assertSame(['my_group'], $cConstraint->groups); + self::assertSame('some attached data', $cConstraint->payload); + + /** @var Choice $stringIndexedConstraint */ + [$stringIndexedConstraint] = $metadata->properties['stringIndexed']->getConstraints(); + self::assertSame(['one' => 1, 'two' => 2], $stringIndexedConstraint->choices); + } +} + +class ChoiceDummy +{ + #[Choice(choices: [1, 2])] + private $a; + + #[Choice(choices: ['foo', 'bar'], message: 'myMessage')] + private $b; + + #[Choice([1, 2], groups: ['my_group'], payload: 'some attached data')] + private $c; + + #[Choice(choices: ['one' => 1, 'two' => 2])] + private $stringIndexed; } diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 0476fa793cdff..e3d638c95e4e4 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -19,6 +19,7 @@ "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php81": "^1.22", "symfony/translation-contracts": "^1.1|^2|^3" }, "require-dev": { diff --git a/src/Symfony/Component/VarDumper/LICENSE b/src/Symfony/Component/VarDumper/LICENSE index c1f0aac1c5614..a843ec124ea70 100644 --- a/src/Symfony/Component/VarDumper/LICENSE +++ b/src/Symfony/Component/VarDumper/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2021 Fabien Potencier +Copyright (c) 2014-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/RdKafkaCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/RdKafkaCasterTest.php index 48db493396500..7d86dfbdeacfc 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/RdKafkaCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/RdKafkaCasterTest.php @@ -77,9 +77,7 @@ public function testDumpProducer() $expectedDump = <<broker/1001" brokers: RdKafka\Metadata\Collection { @@ -146,10 +144,7 @@ public function testDumpKafkaConsumer() $expectedDump = << "test-topic" ] assignment: [] diff --git a/src/Symfony/Component/VarExporter/LICENSE b/src/Symfony/Component/VarExporter/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Component/VarExporter/LICENSE +++ b/src/Symfony/Component/VarExporter/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/WebLink/LICENSE b/src/Symfony/Component/WebLink/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/WebLink/LICENSE +++ b/src/Symfony/Component/WebLink/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Workflow/LICENSE b/src/Symfony/Component/Workflow/LICENSE index c1f0aac1c5614..a843ec124ea70 100644 --- a/src/Symfony/Component/Workflow/LICENSE +++ b/src/Symfony/Component/Workflow/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2021 Fabien Potencier +Copyright (c) 2014-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 104b7b0ae1f4c..88ae6796e5521 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -620,21 +620,18 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer return (float) substr($scalar, 8); case 0 === strpos($scalar, '!!binary '): return self::evaluateBinaryScalar(substr($scalar, 9)); - default: - throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename); } - // no break + + throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename); case preg_match('/^(?:\+|-)?0o(?P[0-7_]++)$/', $scalar, $matches): $value = str_replace('_', '', $matches['value']); if ('-' === $scalar[0]) { return -octdec($value); - } else { - return octdec($value); } + return octdec($value); // Optimize for returning strings. - // no break case \in_array($scalar[0], ['+', '-', '.'], true) || is_numeric($scalar[0]): if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) { $scalar = str_replace('_', '', $scalar); diff --git a/src/Symfony/Component/Yaml/LICENSE b/src/Symfony/Component/Yaml/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Yaml/LICENSE +++ b/src/Symfony/Component/Yaml/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2021 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 2b080dee63792..b570e12264a03 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -85,6 +85,8 @@ public function parse(string $value, int $flags = 0): mixed try { $data = $this->doParse($value, $flags); } finally { + $this->refsBeingParsed = []; + $this->offset = 0; $this->lines = []; $this->currentLine = ''; $this->numberOfParsedLines = 0; diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 50ed706f1ae46..769af36eaa6dd 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -84,6 +84,20 @@ public function invalidIndentation(): array ]; } + public function testParserIsStateless() + { + $yamlString = '# translations/messages.en.yaml + +'; + $this->parser->parse($yamlString); + $this->parser->parse($yamlString); + + $this->expectException(ParseException::class); + $this->expectExceptionMessage("A YAML file cannot contain tabs as indentation at line 2 (near \"\tabc\")"); + + $this->parser->parse("abc:\n\tabc"); + } + /** * @dataProvider validTokenSeparators */ diff --git a/src/Symfony/Contracts/Cache/LICENSE b/src/Symfony/Contracts/Cache/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Contracts/Cache/LICENSE +++ b/src/Symfony/Contracts/Cache/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Contracts/Deprecation/LICENSE b/src/Symfony/Contracts/Deprecation/LICENSE index ad85e1737485d..406242ff28554 100644 --- a/src/Symfony/Contracts/Deprecation/LICENSE +++ b/src/Symfony/Contracts/Deprecation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Fabien Potencier +Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Contracts/EventDispatcher/LICENSE b/src/Symfony/Contracts/EventDispatcher/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Contracts/EventDispatcher/LICENSE +++ b/src/Symfony/Contracts/EventDispatcher/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Contracts/HttpClient/LICENSE b/src/Symfony/Contracts/HttpClient/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Contracts/HttpClient/LICENSE +++ b/src/Symfony/Contracts/HttpClient/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Contracts/LICENSE b/src/Symfony/Contracts/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Contracts/LICENSE +++ b/src/Symfony/Contracts/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Contracts/Service/LICENSE b/src/Symfony/Contracts/Service/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Contracts/Service/LICENSE +++ b/src/Symfony/Contracts/Service/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Contracts/Translation/LICENSE b/src/Symfony/Contracts/Translation/LICENSE index 2358414536d95..74cdc2dbf6dbe 100644 --- a/src/Symfony/Contracts/Translation/LICENSE +++ b/src/Symfony/Contracts/Translation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Fabien Potencier +Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal