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/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 7961ca61254a5..36e769ce13309 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 cfbc9200082de..dbe232eaef6d3 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -214,6 +214,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 == '7.2' && ! matrix.mode" run: | diff --git a/CHANGELOG-5.3.md b/CHANGELOG-5.3.md index ceeefa8cad098..0436dd92be510 100644 --- a/CHANGELOG-5.3.md +++ b/CHANGELOG-5.3.md @@ -7,6 +7,69 @@ in 5.3 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.3.0...v5.3.1 +* 5.3.13 (2021-12-29) + + * bug #44838 [DependencyInjection][HttpKernel] Fix enum typed bindings (ogizanagi) + * bug #44723 [Lock] Release PostgreSqlStore connection lock on failure (simon-watiau) * commit 'e5b2f9efba': [Lock] Release PostgreSqlStore connection lock on failure + * bug #44826 [HttpKernel] Do not attempt to register enum arguments in controller service locator (ogizanagi) + * bug #44824 [Mime] Fix missing sprintf in DkimSigner (alamirault) + * bug #44816 [Translation] [LocoProvider] Use rawurlencode and separate tag setting (danut007ro) + * bug #44805 [Security] fix unserializing session payloads from v4 (nicolas-grekas) + * bug #44820 [Cache] Don't lock when doing nested computations (nicolas-grekas) + * bug #44807 [Messenger] fix Redis support on 32b arch (nicolas-grekas) + * bug #44759 [HttpFoundation] Fix notice when HTTP_PHP_AUTH_USER passed without pass (Vitali Tsyrkin) + * bug #44799 [Cache] fix compat with apcu < 5.1.10 (nicolas-grekas) + * bug #44085 [Translation] Fix TranslationPullCommand with ICU translations (Kocal) + * bug #44771 [Notifier] Use correct factory for the msteams transport (veewee) + * bug #44618 [HttpKernel] Fix SessionListener without session in request (shyim) + * bug #44743 [HttpClient] fix checking for recent curl consts (nicolas-grekas) + * bug #44752 [Security/Http] Fix cookie clearing on logout (maxhelias) + * bug #44732 [Mime] Relaxing in-reply-to header validation (ThomasLandauer) + * bug #44728 [Mime] Fix encoding filenames in multipart/form-data (nicolas-grekas) + * bug #44710 [DependencyInjection] fix linting callable classes (nicolas-grekas) + * bug #44639 [DependencyInjection] Cast tag attribute value to string (ruudk) + * bug #44473 [Validator] Restore default locale in ConstraintValidatorTestCase (rodnaph) + * bug #44682 [FrameworkBundle] alias `cache.app.taggable` to `cache.app` if using `cache.adapter.redis_tag_aware` (kbond) + * bug #44671 [HttpClient] Fix tracing requests made after calling withOptions() (nicolas-grekas) + * bug #44577 [Cache] Fix proxy no expiration to the Redis (Sergey Belyshkin) + * bug #44669 [Cache] disable lock on CLI (nicolas-grekas) + * bug #44598 [Translation] Handle the blank-translation in Loco Adapter (kgonella) + * bug #44354 [RateLimiter] Make RateLimiter resilient to timeShifting (jderusse) + * bug #44600 [Serializer] Fix denormalizing custom class in UidNormalizer (fancyweb) + * bug #44537 [Config] In XmlUtils, avoid converting from octal every string starting with a 0 (alexandre-daubois) + * bug #44510 [Workflow] Fix eventsToDispatch parameter setup for StateMachine (Olexandr Kalaidzhy) + * bug #44625 [HttpClient] fix monitoring responses issued before reset() (nicolas-grekas) + * bug #44623 [HttpClient] Fix dealing with "HTTP/1.1 000 " responses (nicolas-grekas) + * bug #44601 [HttpClient] Fix closing curl-multi handle too early on destruct (nicolas-grekas) + * bug #44571 [HttpClient] Don't reset timeout counter when initializing requests (nicolas-grekas) + * bug #44479 [HttpClient] Double check if handle is complete (Nyholm) + * bug #44418 [DependencyInjection] Resolve ChildDefinition in AbstractRecursivePass (fancyweb) + * bug #44474 [Translation] [Bridge] [Lokalise] Fix push keys to lokalise. Closes #… (olegmifle) + * bug #43164 [FrameworkBundle] Fix cache pool configuration with one adapter and one provider (fancyweb) + * bug #44419 [PropertyAccess] Fix accessing public property on Object (kevcomparadise) + * bug #44565 [FrameworkBundle] Use correct cookie domain in loginUser() (wouterj) + * bug #44538 [Process] fixed uppercase ARGC and ARGV should also be skipped (rbaarsma) + * bug #44438 [HttpClient] Fix handling thrown \Exception in \Generator in MockResponse (fancyweb) + * bug #44469 [String] Fix requiring wcswitch table several times (fancyweb) + * bug #44502 [HttpFoundation] do not call preg_match() on null (xabbuh) + * bug #44481 [FrameworkBundle] Fix loginUser() causing deprecation (wouterj) + * bug #44416 [Translation] Make http requests synchronous when reading the Loco API (Kocal) + * bug #44350 [Translation] Fix TranslationTrait (Tomasz Kusy) + * bug #44467 [Console] Fix parameter types for `ProcessHelper::mustRun()` (derrabus) + * bug #44427 [FrameworkBundle] Fix compatibility with symfony/security-core 6.x (deps=high tests) (wouterj) + * bug #44399 Prevent infinite nesting of lazy `ObjectManager` instances when `ObjectManager` is reset (Ocramius) + * bug #44375 [DoctrineBridge] fix calling get_class on non-object (kbond) + * bug #44361 [HttpClient] Fix handling error info in MockResponse (fancyweb) + * bug #44309 [Messenger] Leverage DBAL's getNativeConnection() method (derrabus) + * bug #44187 [Translation] [Loco] Fix idempotency of LocoProvider write method (welcoMattic) + * bug #43992 [Security] Do not overwrite already stored tokens for REMOTE_USER authentication (stlrnz) + * bug #43876 [Validator] Fix validation for single level domains (HypeMC) + * bug #44327 [Debug][ErrorHandler] Increased the reserved memory from 10k to 32k (sakalys) + * bug #44261 [Process] intersect with getenv() in case-insensitive manner to get default envs (stable-staple) + * bug #44295 [Serializer] fix support for lazy/unset properties (nicolas-grekas) + * bug #44277 [Notifier] Fix AllMySms bridge body content (afiocre) + * bug #44269 [DoctrineBridge] Revert " add support for the JSON type" (dunglas) + * 5.3.12 (2021-11-24) * security #cve-2021-41268 [SecurityBundle] Default signature_properties to the previous behavior (wouterj) diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md index 560763c768582..f7c1ec3aa8cf9 100644 --- a/CHANGELOG-5.4.md +++ b/CHANGELOG-5.4.md @@ -7,6 +7,51 @@ in 5.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.4.0...v5.4.1 +* 5.4.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 #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) + * 5.4.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-5.4.md b/UPGRADE-5.4.md index d7492d23f3cfb..4d0a01e129216 100644 --- a/UPGRADE-5.4.md +++ b/UPGRADE-5.4.md @@ -134,7 +134,7 @@ Security * Deprecate the `$authManager` argument of `AccessListener`, the argument will be removed * Deprecate the `$authenticationManager` argument of the `AuthorizationChecker` constructor, the argument will be removed * Deprecate setting the `$alwaysAuthenticate` argument to `true` and not setting the - `$exceptionOnNoToken argument to `false` of `AuthorizationChecker` (this is the default + `$exceptionOnNoToken` argument to `false` of `AuthorizationChecker` (this is the default behavior when using `enable_authenticator_manager: true`) * Deprecate not setting the `$exceptionOnNoToken` argument of `AccessListener` to `false` (this is the default behavior when using `enable_authenticator_manager: true`) 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 d64d859d766f2..2f8b36e9d0fc1 100644 --- a/composer.json +++ b/composer.json @@ -165,7 +165,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 ce71235bea2b9..bc610bedd86ff 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -148,10 +148,15 @@ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \Re $bundleDir = func_get_arg(3); } - $bundleDir ?? $bundleDir = \dirname($bundle->getFileName()); + $bundleClassDir = \dirname($bundle->getFileName()); + $bundleDir ?? $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']) { @@ -161,7 +166,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 8148ce0d3882d..6955becfd2916 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 b665b242cc496..294767b987fac 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,75 @@ public function testUnrecognizedCacheDriverException() $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); } + public function providerBundles() + { + yield ['AnnotationsBundle', 'annotation', '/Entity']; + if (\PHP_VERSION_ID >= 80000) { + 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 +354,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 b3ab046ebd42b..7ea316f41a2d0 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 c7a5fd7a62d03..870e686de934e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -96,8 +96,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 d33554c311fb1..d4e4a575d40d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -341,34 +341,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'], true); - } - $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".'); @@ -501,6 +477,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'], true); + } + $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 8e3bb4ce2abed..c07028847d095 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -272,6 +272,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 dfb2589cba315..cb7d79dfbfe40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php @@ -99,6 +99,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) @@ -178,6 +179,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/Psr16Cache.php b/src/Symfony/Component/Cache/Psr16Cache.php index c785144b1bf70..28c7de60564f8 100644 --- a/src/Symfony/Component/Cache/Psr16Cache.php +++ b/src/Symfony/Component/Cache/Psr16Cache.php @@ -19,6 +19,10 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Traits\ProxyTrait; +if (null !== (new \ReflectionMethod(CacheInterface::class, 'get'))->getReturnType()) { + throw new \LogicException('psr/simple-cache 3.0+ is not compatible with this version of symfony/cache. Please upgrade symfony/cache to 6.0+ or downgrade psr/simple-cache to 1.x or 2.x.'); +} + /** * Turns a PSR-6 cache into a PSR-16 one. * 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 9a491adb5acb8..9fdb9319edd34 100644 --- a/src/Symfony/Component/Cache/Traits/ContractsTrait.php +++ b/src/Symfony/Component/Cache/Traits/ContractsTrait.php @@ -42,7 +42,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 8e45081aefda7..c06cc309a6b3f 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 aacd8834b7df9..6347bde71fad8 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -189,7 +189,7 @@ public static function createConnection(string $dsn, array $options = []) $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 f0b7a5931e29a..14387b51bbdee 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -91,7 +91,7 @@ public function ifNull() /** * Tests if the value is empty. * - * @return ExprBuilder + * @return $this */ public function ifEmpty() { 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 735b1ef18ee36..a81cfdcbbc4d3 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -979,6 +979,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 83ec49a8bdac0..02cc8d76a199c 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -34,13 +34,13 @@ public function __clone() } /** - * Escapes "<" special char in given text. + * Escapes "<" and ">" special chars in given text. * * @return string */ public static function escape(string $text) { - $text = preg_replace('/([^\\\\]?)])/', '$1\\\\$2', $text); return self::escapeTrailingBackslash($text); } @@ -142,9 +142,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]; @@ -178,11 +179,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" => '\\', '\\<' => '<', '\\>' => '>']); } /** @@ -216,7 +213,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 7abd092ec858d..842a618ef6129 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -251,6 +251,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'); @@ -260,11 +263,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) { @@ -369,7 +376,7 @@ function ($match) use ($ret) { } // Reset stty so it behaves normally again - shell_exec(sprintf('stty %s', $sttyMode)); + shell_exec('stty '.$sttyMode); return $fullChoice; } @@ -430,7 +437,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) { @@ -485,11 +492,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 92b6e8663fdc3..bf1f904878d9f 100644 --- a/src/Symfony/Component/Console/Question/ChoiceQuestion.php +++ b/src/Symfony/Component/Console/Question/ChoiceQuestion.php @@ -125,18 +125,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 fe1f27fcc83d5..a5918aa3fc81b 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 18063eaac90d7..a7a2dfba487a6 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 f8ec9214de425..b7ec85cefb489 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, $value, \ReflectionPar $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 c71ea503bfc0b..5bc379153a19e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -126,7 +126,7 @@ protected function processValue($value, bool $isRoot = false) $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 a1b2c86e3d0de..46a2d7882cf89 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 632c910e396e8..485630a748764 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1413,7 +1413,7 @@ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs 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 8a454e9f00722..77ac210217cf9 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) } 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 573b6f53a291d..3021e07088ddd 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, $valueOrRef): self { $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 38538f27b0f9f..97e167c99cb10 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -996,6 +996,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 3ddad62d0e5d7..fc48bf0723312 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); + } + /** * @requires PHP 8 */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php index f35d7af581b7b..887e2b1524855 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php @@ -421,4 +421,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 0b84145beda02..5885a9190b615 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 4ef80ddaab46c..c324e9c13308a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -97,6 +97,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 f249ca8e3944c..2c76d52c809e8 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -151,7 +151,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 53dd4ee8711d2..b44d217ea3214 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 2821e10df8465..d18a3d36b1959 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -171,7 +171,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(); } @@ -288,6 +287,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) { @@ -311,9 +311,9 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of CurlResponse objects, "%s" given.', __METHOD__, get_debug_type($responses))); } - if (\is_resource($mh = $this->multi->handles[0] ?? null) || $mh instanceof \CurlMultiHandle) { + if (\is_resource($this->multi->handle) || $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 94a132706028c..d73a5058830fb 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 0ff4fa03e09c4..ec3b01d999748 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; @@ -75,7 +76,7 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI $this->client = $client ?? HttpClient::create(); $this->responseFactory = $responseFactory; $this->streamFactory = $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 === $this->responseFactory || null === $this->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 065e05e173c83..958a00a8819cb 100644 --- a/src/Symfony/Component/HttpClient/Internal/CurlClientState.php +++ b/src/Symfony/Component/HttpClient/Internal/CurlClientState.php @@ -23,8 +23,10 @@ */ final class CurlClientState extends ClientState { - /** @var array<\CurlMultiHandle|resource> */ - public $handles = []; + /** @var \CurlMultiHandle|resource|null */ + public $handle; + /** @var \CurlShareHandle|resource|null */ + public $share; /** @var PushedResponse[] */ public $pushedResponses = []; /** @var DnsCache */ @@ -37,27 +39,23 @@ final class CurlClientState extends ClientState public static $curlVersion; - private $maxHostConnections; - private $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 @@ -72,14 +70,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); }); } @@ -88,10 +86,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); } @@ -99,11 +94,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 48af4cbb0dc05..9f5658f560fbc 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 6c64d2d00f058..c7c2b09f2a439 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -106,9 +106,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null, 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); @@ -120,7 +118,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null, } 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); } }; @@ -174,7 +172,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null, // 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]); @@ -184,9 +182,7 @@ public function __construct(CurlClientState $multi, $ch, array $options = null, 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, @@ -268,7 +264,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]]; @@ -301,47 +297,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; @@ -371,11 +359,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 2231464a229e8..2efacca763b8b 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 52f263a8af3c7..284a243496b91 100644 --- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php @@ -66,9 +66,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 7f69ed79ccc76..7c0c1aee3ac17 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -75,10 +75,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); } @@ -152,7 +154,8 @@ public function onKernelResponse(ResponseEvent $event) $request = $event->getRequest(); $requestSessionCookieId = $request->cookies->get($sessionName); - if ($requestSessionCookieId && $session->isEmpty()) { + $isSessionEmpty = $session->isEmpty() && empty($_SESSION); // checking $_SESSION to keep compatibility with native sessions + if ($requestSessionCookieId && $isSessionEmpty) { $response->headers->clearCookie( $sessionName, $sessionCookiePath, @@ -161,7 +164,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 3edc5fe6d383a..9125b7969cbb8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static $freshCache = []; - public const VERSION = '5.4.2'; - public const VERSION_ID = 50402; + public const VERSION = '5.4.3'; + public const VERSION_ID = 50403; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 2; + public const RELEASE_VERSION = 3; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2024'; 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 45f479df34d30..ca49b42d41673 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\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -125,6 +128,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); @@ -572,4 +736,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/Inflector/LICENSE b/src/Symfony/Component/Inflector/LICENSE index 28c5b8066c3c1..9f862df305075 100644 --- a/src/Symfony/Component/Inflector/LICENSE +++ b/src/Symfony/Component/Inflector/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2021 Fabien Potencier +Copyright (c) 2012-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/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 e490116443795..fddfffef9084f 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -191,7 +191,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $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 163b1fa5e4d48..b34f9046b5e2b 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/Nexmo/LICENSE b/src/Symfony/Component/Notifier/Bridge/Nexmo/LICENSE index 383e7a54586e7..9c907a46a6218 100644 --- a/src/Symfony/Component/Notifier/Bridge/Nexmo/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Nexmo/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 700447dfdf3a7..e45ae6c0d1c3c 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 015816c4fdd35..0105100562849 100644 --- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php +++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php @@ -47,7 +47,9 @@ public function __construct($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 5d9298df7a52d..dbea0c153ee1b 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -482,11 +482,11 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid } } catch (\Error $e) { // handle uninitialized properties in PHP >= 7.4 - if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { - $r = new \ReflectionProperty($matches[1], $matches[2]); + if (\PHP_VERSION_ID >= 70400 && 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 521218f671c75..c0fb51bf9053e 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; 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; @@ -191,7 +190,7 @@ 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() { @@ -206,9 +205,44 @@ public function getUninitialized(): array $this->propertyAccessor->getValue($object, 'uninitialized'); } + /** + * @requires PHP 7.4 + */ + 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 = eval('return new class() { + private string $uninitialized; + + public function getUninitialized(): string + { + return $this->uninitialized; + } + };'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + /** + * @requires PHP 7.4 + */ + 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 = eval('return 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 { @@ -225,7 +259,7 @@ public function getUninitialized(): array 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 {};'); 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 cb27197f9ee87..7bc85e522613b 100644 --- a/src/Symfony/Component/RateLimiter/Policy/SlidingWindow.php +++ b/src/Symfony/Component/RateLimiter/Policy/SlidingWindow.php @@ -122,6 +122,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 ccc6a79a91baa..ee07445bef983 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 fb8b8421133d0..52a8ce860caf2 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/Guard/LICENSE b/src/Symfony/Component/Security/Guard/LICENSE index 9ff2d0d6306da..88bf75bb4d6a2 100644 --- a/src/Symfony/Component/Security/Guard/LICENSE +++ b/src/Symfony/Component/Security/Guard/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/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 245c16ca6abdb..ef3714e6cd1d8 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -573,6 +573,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/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index fa9483ccf6397..3e0800ea20154 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 @@ -761,6 +762,25 @@ public function testExtractAttributesRespectsContext() $this->assertSame(['foo' => 'bar', 'bar' => 'foo'], $normalizer->normalize($data, null, ['include_foo_and_bar' => true])); } + /** + * @requires PHP 8 + */ + 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/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 5bd19242cbcd4..ebd23c3e3dfdf 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -34,7 +34,7 @@ "symfony/http-kernel": "^4.4|^5.0|^6.0", "symfony/mime": "^4.4|^5.0|^6.0", "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.3|^6.0", + "symfony/property-info": "^5.3.13|^6.0", "symfony/uid": "^5.3|^6.0", "symfony/validator": "^4.4|^5.0|^6.0", "symfony/var-dumper": "^4.4|^5.0|^6.0", @@ -47,7 +47,7 @@ "phpdocumentor/type-resolver": "<1.4.0", "symfony/dependency-injection": "<4.4", "symfony/property-access": "<5.4", - "symfony/property-info": "<5.3", + "symfony/property-info": "<5.3.13", "symfony/uid": "<5.3", "symfony/yaml": "<4.4" }, 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 194679d2805e6..8709a8969ce20 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 7045a9e69f7d4..52da595c602db 100644 --- a/src/Symfony/Component/Translation/Command/TranslationPullCommand.php +++ b/src/Symfony/Component/Translation/Command/TranslationPullCommand.php @@ -111,7 +111,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 628a06b3fd3a8..56c6f5b1c59bf 100644 --- a/src/Symfony/Component/Translation/Command/TranslationPushCommand.php +++ b/src/Symfony/Component/Translation/Command/TranslationPushCommand.php @@ -103,7 +103,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 2b6014aa540f4..27ddcb8891309 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -233,6 +233,10 @@ public function __isset(string $option) */ 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 1e37c31f4388b..b5bcc6d3d9864 100644 --- a/src/Symfony/Component/Validator/Constraints/Choice.php +++ b/src/Symfony/Component/Validator/Constraints/Choice.php @@ -52,7 +52,8 @@ public function getDefaultOption() } public function __construct( - $choices = null, + $options = [], + array $choices = null, $callback = null, bool $multiple = null, bool $strict = null, @@ -63,12 +64,13 @@ public function __construct( string $minMessage = null, string $maxMessage = null, $groups = null, - $payload = null, - array $options = [] + $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 = $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..e94bffbf28d3e 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,50 @@ public function testSetDefaultPropertyChoice() self::assertEquals(['A', 'B', 'C'], $constraint->choices); } + + /** + * @requires PHP 8 + */ + 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 40d942a1dca61..0ce4d0d2a4318 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -22,6 +22,7 @@ "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "~1.0", "symfony/polyfill-php80": "^1.16", + "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 e005666c173bc..3d64b1ada4464 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -646,21 +646,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); @@ -669,7 +666,7 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer switch (true) { case ctype_digit($scalar): if (preg_match('/^0[0-7]+$/', $scalar)) { - trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0.'); + trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', '0o'.substr($scalar, 1)); return octdec($scalar); } @@ -679,7 +676,7 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer return ($scalar === (string) $cast) ? $cast : $scalar; case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): if (preg_match('/^-0[0-7]+$/', $scalar)) { - trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0.'); + trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', '-0o'.substr($scalar, 2)); return -octdec(substr($scalar, 1)); } 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 4b642e3ac8c92..2701a4418c52f 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -99,6 +99,8 @@ public function parse(string $value, int $flags = 0) if (null !== $mbEncoding) { mb_internal_encoding($mbEncoding); } + $this->refsBeingParsed = []; + $this->offset = 0; $this->lines = []; $this->currentLine = ''; $this->numberOfParsedLines = 0; diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 76c2ef114944f..5d338a41faa62 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -771,9 +771,9 @@ public function getTestsForOctalNumbers() * @group legacy * @dataProvider getTestsForOctalNumbersYaml11Notation */ - public function testParseOctalNumbersYaml11Notation(int $expected, string $yaml) + public function testParseOctalNumbersYaml11Notation(int $expected, string $yaml, string $replacement) { - $this->expectDeprecation('Since symfony/yaml 5.1: Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0.'); + $this->expectDeprecation(sprintf('Since symfony/yaml 5.1: Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', $replacement)); self::assertSame($expected, Inline::parse($yaml)); } @@ -781,9 +781,9 @@ public function testParseOctalNumbersYaml11Notation(int $expected, string $yaml) public function getTestsForOctalNumbersYaml11Notation() { return [ - 'positive octal number' => [28, '034'], - 'positive octal number with separator' => [1243, '0_2_3_3_3'], - 'negative octal number' => [-28, '-034'], + 'positive octal number' => [28, '034', '0o34'], + 'positive octal number with separator' => [1243, '0_2_3_3_3', '0o2333'], + 'negative octal number' => [-28, '-034', '-0o34'], ]; } 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