From 244b8115d04a446fb8ce5bf63cf6612e5ab2185f Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 29 Jan 2025 16:30:30 +0100 Subject: [PATCH 1/2] build(deps): Bump symfony/* to latest 5.4 Signed-off-by: Joas Schilling --- composer.json | 14 +-- composer.lock | 92 ++++++++-------- composer/installed.json | 100 +++++++++--------- composer/installed.php | 42 ++++---- symfony/console/Application.php | 6 +- .../console/Completion/CompletionInput.php | 2 +- symfony/console/Helper/ProgressBar.php | 5 +- symfony/console/Helper/QuestionHelper.php | 14 +-- symfony/console/Helper/Table.php | 10 +- symfony/console/Input/InputOption.php | 2 +- symfony/console/Output/StreamOutput.php | 45 +++----- symfony/console/Resources/completion.bash | 2 +- symfony/console/Terminal.php | 3 +- .../Debug/TraceableEventDispatcher.php | 14 +-- .../Debug/WrappedListener.php | 4 +- symfony/event-dispatcher/EventDispatcher.php | 6 +- .../EventDispatcherInterface.php | 4 +- symfony/event-dispatcher/GenericEvent.php | 2 +- .../ImmutableEventDispatcher.php | 6 +- .../http-foundation/BinaryFileResponse.php | 14 ++- symfony/http-foundation/Cookie.php | 4 +- .../Exception/SessionNotFoundException.php | 2 +- symfony/http-foundation/File/File.php | 4 +- symfony/http-foundation/File/UploadedFile.php | 8 +- symfony/http-foundation/HeaderBag.php | 6 +- symfony/http-foundation/HeaderUtils.php | 65 ++++++------ symfony/http-foundation/InputBag.php | 2 +- symfony/http-foundation/JsonResponse.php | 2 +- symfony/http-foundation/ParameterBag.php | 2 +- symfony/http-foundation/RedirectResponse.php | 1 + symfony/http-foundation/Request.php | 56 ++++++++-- symfony/http-foundation/RequestMatcher.php | 4 +- symfony/http-foundation/Response.php | 12 +-- symfony/http-foundation/ResponseHeaderBag.php | 6 +- symfony/http-foundation/ServerBag.php | 4 +- symfony/http-foundation/Session/Session.php | 6 +- .../Session/SessionFactory.php | 2 +- .../Session/SessionInterface.php | 4 +- .../Handler/NativeFileSessionHandler.php | 2 +- .../Storage/Handler/PdoSessionHandler.php | 8 +- .../Storage/Handler/SessionHandlerFactory.php | 5 +- .../Session/Storage/MetadataBag.php | 4 +- .../Storage/MockArraySessionStorage.php | 11 +- .../Storage/MockFileSessionStorage.php | 4 +- .../Storage/MockFileSessionStorageFactory.php | 2 +- .../Session/Storage/NativeSessionStorage.php | 8 +- .../Storage/NativeSessionStorageFactory.php | 2 +- .../Storage/PhpBridgeSessionStorage.php | 2 +- .../PhpBridgeSessionStorageFactory.php | 2 +- .../Storage/SessionStorageInterface.php | 2 +- symfony/http-foundation/StreamedResponse.php | 2 +- .../DataCollector/MessageDataCollector.php | 2 +- symfony/mailer/Event/MessageEvents.php | 4 +- .../mailer/EventListener/EnvelopeListener.php | 2 +- .../mailer/EventListener/MessageListener.php | 2 +- .../Exception/HttpTransportException.php | 2 +- .../Exception/UnsupportedSchemeException.php | 2 +- symfony/mailer/Mailer.php | 4 +- symfony/mailer/MailerInterface.php | 4 +- symfony/mailer/Messenger/SendEmailMessage.php | 2 +- symfony/mailer/Transport.php | 8 +- .../Transport/AbstractHttpTransport.php | 2 +- .../mailer/Transport/AbstractTransport.php | 6 +- .../Transport/AbstractTransportFactory.php | 2 +- symfony/mailer/Transport/Dsn.php | 26 ++--- .../mailer/Transport/RoundRobinTransport.php | 2 +- .../mailer/Transport/SendmailTransport.php | 5 +- .../mailer/Transport/Smtp/EsmtpTransport.php | 2 +- .../mailer/Transport/Smtp/SmtpTransport.php | 4 +- .../Transport/Smtp/Stream/AbstractStream.php | 8 +- .../Transport/Smtp/Stream/ProcessStream.php | 19 +++- .../Transport/Smtp/Stream/SocketStream.php | 2 +- .../mailer/Transport/TransportInterface.php | 2 +- symfony/mailer/Transport/Transports.php | 2 +- symfony/process/ExecutableFinder.php | 61 +++++++---- symfony/process/InputStream.php | 2 +- symfony/process/PhpExecutableFinder.php | 11 +- symfony/process/PhpProcess.php | 6 +- symfony/process/Process.php | 48 ++++++--- symfony/routing/Annotation/Route.php | 18 ++-- symfony/routing/CompiledRoute.php | 2 +- .../Exception/MethodNotAllowedException.php | 2 +- .../Generator/CompiledUrlGenerator.php | 2 +- symfony/routing/Generator/UrlGenerator.php | 2 +- .../routing/Loader/AnnotationClassLoader.php | 6 +- .../Loader/AnnotationDirectoryLoader.php | 4 +- .../routing/Loader/AnnotationFileLoader.php | 6 +- symfony/routing/Loader/ClosureLoader.php | 4 +- .../Configurator/CollectionConfigurator.php | 2 +- .../Loader/Configurator/RouteConfigurator.php | 2 +- .../Configurator/RoutingConfigurator.php | 4 +- .../Loader/Configurator/Traits/HostTrait.php | 5 +- .../Traits/LocalizedRouteTrait.php | 2 +- .../Configurator/Traits/PrefixTrait.php | 5 +- symfony/routing/Loader/ContainerLoader.php | 4 +- symfony/routing/Loader/DirectoryLoader.php | 4 +- symfony/routing/Loader/GlobFileLoader.php | 4 +- symfony/routing/Loader/ObjectLoader.php | 2 +- symfony/routing/Loader/PhpFileLoader.php | 4 +- symfony/routing/Loader/XmlFileLoader.php | 4 +- symfony/routing/Loader/YamlFileLoader.php | 4 +- .../Dumper/CompiledUrlMatcherDumper.php | 6 +- .../Matcher/Dumper/StaticPrefixCollection.php | 3 +- .../RedirectableUrlMatcherInterface.php | 2 +- .../routing/Matcher/TraceableUrlMatcher.php | 2 +- symfony/routing/RouteCollection.php | 20 ++++ symfony/routing/RouteCollectionBuilder.php | 10 +- symfony/routing/Router.php | 4 +- .../translation/Command/XliffLintCommand.php | 4 +- .../TranslationDataCollector.php | 2 +- .../translation/DataCollectorTranslator.php | 4 +- symfony/translation/Dumper/CsvFileDumper.php | 2 +- .../translation/Dumper/XliffFileDumper.php | 2 +- .../Exception/IncompleteDsnException.php | 2 +- .../MissingRequiredOptionException.php | 2 +- .../Exception/ProviderException.php | 2 +- .../Exception/UnsupportedSchemeException.php | 2 +- .../Extractor/PhpStringTokenParser.php | 2 +- .../Formatter/MessageFormatter.php | 2 +- symfony/translation/Loader/CsvFileLoader.php | 8 +- .../translation/Loader/IcuResFileLoader.php | 2 +- .../translation/Loader/XliffFileLoader.php | 18 +++- symfony/translation/LoggingTranslator.php | 4 +- symfony/translation/MessageCatalogue.php | 2 +- .../translation/MessageCatalogueInterface.php | 2 +- .../Provider/AbstractProviderFactory.php | 2 +- symfony/translation/Provider/Dsn.php | 28 ++--- .../PseudoLocalizationTranslator.php | 4 +- .../translation/Resources/data/parents.json | 1 + symfony/translation/Resources/functions.php | 2 +- symfony/translation/TranslatableMessage.php | 4 +- symfony/translation/Translator.php | 8 +- symfony/translation/TranslatorBag.php | 2 +- .../translation/TranslatorBagInterface.php | 2 +- symfony/translation/Util/ArrayConverter.php | 47 +++++++- 135 files changed, 662 insertions(+), 512 deletions(-) diff --git a/composer.json b/composer.json index 190440ff0..ac576e112 100644 --- a/composer.json +++ b/composer.json @@ -49,15 +49,15 @@ "sabre/dav": "^4.4.0", "scssphp/scssphp": "^1.12", "stecman/symfony-console-completion": "^0.11.0", - "symfony/console": "^5.4.24", - "symfony/event-dispatcher": "^5.4.26", - "symfony/http-foundation": "^5.4.24", - "symfony/mailer": "^5.4.22", + "symfony/console": "^5.4.47", + "symfony/event-dispatcher": "^5.4.45", + "symfony/http-foundation": "^5.4.48", + "symfony/mailer": "^5.4.45", "symfony/polyfill-intl-grapheme": "^1.28", "symfony/polyfill-intl-normalizer": "^1.28", - "symfony/process": "^5.4.34", - "symfony/routing": "^5.4.24", - "symfony/translation": "^5.4.24", + "symfony/process": "^5.4.47", + "symfony/routing": "^5.4.48", + "symfony/translation": "^5.4.45", "wapmorgan/mp3info": "^0.1.0", "web-auth/webauthn-lib": "^3.1" }, diff --git a/composer.lock b/composer.lock index 80f4b4dd0..edc9b10f5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cc85aa0d53fea4e633696ffc23e64459", + "content-hash": "c32e78ef39b8e2781cdb672d6b3c20c8", "packages": [ { "name": "aws/aws-crt-php", @@ -4484,16 +4484,16 @@ }, { "name": "symfony/console", - "version": "v5.4.35", + "version": "v5.4.47", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "dbdf6adcb88d5f83790e1efb57ef4074309d3931" + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dbdf6adcb88d5f83790e1efb57ef4074309d3931", - "reference": "dbdf6adcb88d5f83790e1efb57ef4074309d3931", + "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", "shasum": "" }, "require": { @@ -4563,7 +4563,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.35" + "source": "https://github.com/symfony/console/tree/v5.4.47" }, "funding": [ { @@ -4579,7 +4579,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:28:09+00:00" + "time": "2024-11-06T11:30:55+00:00" }, { "name": "symfony/css-selector", @@ -4791,16 +4791,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v5.4.26", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "5dcc00e03413f05c1e7900090927bb7247cb0aac" + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/5dcc00e03413f05c1e7900090927bb7247cb0aac", - "reference": "5dcc00e03413f05c1e7900090927bb7247cb0aac", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", "shasum": "" }, "require": { @@ -4856,7 +4856,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.26" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45" }, "funding": [ { @@ -4872,7 +4872,7 @@ "type": "tidelift" } ], - "time": "2023-07-06T06:34:20+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4955,16 +4955,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.4.25", + "version": "v5.4.48", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "f66be2706075c5f6325d2fe2b743a57fb5d23f6b" + "reference": "3f38b8af283b830e1363acd79e5bc3412d055341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f66be2706075c5f6325d2fe2b743a57fb5d23f6b", - "reference": "f66be2706075c5f6325d2fe2b743a57fb5d23f6b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f38b8af283b830e1363acd79e5bc3412d055341", + "reference": "3f38b8af283b830e1363acd79e5bc3412d055341", "shasum": "" }, "require": { @@ -4974,7 +4974,7 @@ "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "predis/predis": "~1.0", + "predis/predis": "^1.0|^2.0", "symfony/cache": "^4.4|^5.0|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^4.4|^5.0|^6.0", @@ -5011,7 +5011,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.25" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.48" }, "funding": [ { @@ -5027,20 +5027,20 @@ "type": "tidelift" } ], - "time": "2023-06-22T08:06:06+00:00" + "time": "2024-11-13T18:58:02+00:00" }, { "name": "symfony/mailer", - "version": "v5.4.22", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "6330cd465dfd8b7a07515757a1c37069075f7b0b" + "reference": "f732e1fafdf0f4a2d865e91f1018aaca174aeed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/6330cd465dfd8b7a07515757a1c37069075f7b0b", - "reference": "6330cd465dfd8b7a07515757a1c37069075f7b0b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f732e1fafdf0f4a2d865e91f1018aaca174aeed9", + "reference": "f732e1fafdf0f4a2d865e91f1018aaca174aeed9", "shasum": "" }, "require": { @@ -5087,7 +5087,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v5.4.22" + "source": "https://github.com/symfony/mailer/tree/v5.4.45" }, "funding": [ { @@ -5103,7 +5103,7 @@ "type": "tidelift" } ], - "time": "2023-03-10T10:15:32+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/mime", @@ -5846,16 +5846,16 @@ }, { "name": "symfony/process", - "version": "v5.4.34", + "version": "v5.4.47", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8fa22178dfc368911dbd513b431cd9b06f9afe7a" + "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8fa22178dfc368911dbd513b431cd9b06f9afe7a", - "reference": "8fa22178dfc368911dbd513b431cd9b06f9afe7a", + "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", + "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", "shasum": "" }, "require": { @@ -5888,7 +5888,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.34" + "source": "https://github.com/symfony/process/tree/v5.4.47" }, "funding": [ { @@ -5904,20 +5904,20 @@ "type": "tidelift" } ], - "time": "2023-12-02T08:41:43+00:00" + "time": "2024-11-06T11:36:42+00:00" }, { "name": "symfony/routing", - "version": "v5.4.25", + "version": "v5.4.48", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "56bfc1394f7011303eb2e22724f9b422d3f14649" + "reference": "dd08c19879a9b37ff14fd30dcbdf99a4cf045db1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/56bfc1394f7011303eb2e22724f9b422d3f14649", - "reference": "56bfc1394f7011303eb2e22724f9b422d3f14649", + "url": "https://api.github.com/repos/symfony/routing/zipball/dd08c19879a9b37ff14fd30dcbdf99a4cf045db1", + "reference": "dd08c19879a9b37ff14fd30dcbdf99a4cf045db1", "shasum": "" }, "require": { @@ -5978,7 +5978,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.25" + "source": "https://github.com/symfony/routing/tree/v5.4.48" }, "funding": [ { @@ -5994,7 +5994,7 @@ "type": "tidelift" } ], - "time": "2023-06-05T14:18:47+00:00" + "time": "2024-11-12T18:20:21+00:00" }, { "name": "symfony/service-contracts", @@ -6165,16 +6165,16 @@ }, { "name": "symfony/translation", - "version": "v5.4.24", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "de237e59c5833422342be67402d487fbf50334ff" + "reference": "98f26acc99341ca4bab345fb14d7b1d7cb825bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/de237e59c5833422342be67402d487fbf50334ff", - "reference": "de237e59c5833422342be67402d487fbf50334ff", + "url": "https://api.github.com/repos/symfony/translation/zipball/98f26acc99341ca4bab345fb14d7b1d7cb825bed", + "reference": "98f26acc99341ca4bab345fb14d7b1d7cb825bed", "shasum": "" }, "require": { @@ -6242,7 +6242,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.4.24" + "source": "https://github.com/symfony/translation/tree/v5.4.45" }, "funding": [ { @@ -6258,7 +6258,7 @@ "type": "tidelift" } ], - "time": "2023-05-19T12:34:17+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/translation-contracts", @@ -6741,13 +6741,13 @@ "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^8.0" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.0.2" }, diff --git a/composer/installed.json b/composer/installed.json index 598e92d25..c27ae8a38 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -4685,17 +4685,17 @@ }, { "name": "symfony/console", - "version": "v5.4.35", - "version_normalized": "5.4.35.0", + "version": "v5.4.47", + "version_normalized": "5.4.47.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "dbdf6adcb88d5f83790e1efb57ef4074309d3931" + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dbdf6adcb88d5f83790e1efb57ef4074309d3931", - "reference": "dbdf6adcb88d5f83790e1efb57ef4074309d3931", + "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", "shasum": "" }, "require": { @@ -4733,7 +4733,7 @@ "symfony/lock": "", "symfony/process": "" }, - "time": "2024-01-23T14:28:09+00:00", + "time": "2024-11-06T11:30:55+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -4767,7 +4767,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.35" + "source": "https://github.com/symfony/console/tree/v5.4.47" }, "funding": [ { @@ -5004,17 +5004,17 @@ }, { "name": "symfony/event-dispatcher", - "version": "v5.4.26", - "version_normalized": "5.4.26.0", + "version": "v5.4.45", + "version_normalized": "5.4.45.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "5dcc00e03413f05c1e7900090927bb7247cb0aac" + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/5dcc00e03413f05c1e7900090927bb7247cb0aac", - "reference": "5dcc00e03413f05c1e7900090927bb7247cb0aac", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", "shasum": "" }, "require": { @@ -5044,7 +5044,7 @@ "symfony/dependency-injection": "", "symfony/http-kernel": "" }, - "time": "2023-07-06T06:34:20+00:00", + "time": "2024-09-25T14:11:13+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -5072,7 +5072,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.26" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45" }, "funding": [ { @@ -5174,17 +5174,17 @@ }, { "name": "symfony/http-foundation", - "version": "v5.4.25", - "version_normalized": "5.4.25.0", + "version": "v5.4.48", + "version_normalized": "5.4.48.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "f66be2706075c5f6325d2fe2b743a57fb5d23f6b" + "reference": "3f38b8af283b830e1363acd79e5bc3412d055341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f66be2706075c5f6325d2fe2b743a57fb5d23f6b", - "reference": "f66be2706075c5f6325d2fe2b743a57fb5d23f6b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f38b8af283b830e1363acd79e5bc3412d055341", + "reference": "3f38b8af283b830e1363acd79e5bc3412d055341", "shasum": "" }, "require": { @@ -5194,7 +5194,7 @@ "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "predis/predis": "~1.0", + "predis/predis": "^1.0|^2.0", "symfony/cache": "^4.4|^5.0|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^4.4|^5.0|^6.0", @@ -5205,7 +5205,7 @@ "suggest": { "symfony/mime": "To use the file extension guesser" }, - "time": "2023-06-22T08:06:06+00:00", + "time": "2024-11-13T18:58:02+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -5233,7 +5233,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.25" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.48" }, "funding": [ { @@ -5253,17 +5253,17 @@ }, { "name": "symfony/mailer", - "version": "v5.4.22", - "version_normalized": "5.4.22.0", + "version": "v5.4.45", + "version_normalized": "5.4.45.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "6330cd465dfd8b7a07515757a1c37069075f7b0b" + "reference": "f732e1fafdf0f4a2d865e91f1018aaca174aeed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/6330cd465dfd8b7a07515757a1c37069075f7b0b", - "reference": "6330cd465dfd8b7a07515757a1c37069075f7b0b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f732e1fafdf0f4a2d865e91f1018aaca174aeed9", + "reference": "f732e1fafdf0f4a2d865e91f1018aaca174aeed9", "shasum": "" }, "require": { @@ -5284,7 +5284,7 @@ "symfony/http-client": "^4.4|^5.0|^6.0", "symfony/messenger": "^4.4|^5.0|^6.0" }, - "time": "2023-03-10T10:15:32+00:00", + "time": "2024-09-25T14:11:13+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -5312,7 +5312,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v5.4.22" + "source": "https://github.com/symfony/mailer/tree/v5.4.45" }, "funding": [ { @@ -6098,24 +6098,24 @@ }, { "name": "symfony/process", - "version": "v5.4.34", - "version_normalized": "5.4.34.0", + "version": "v5.4.47", + "version_normalized": "5.4.47.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8fa22178dfc368911dbd513b431cd9b06f9afe7a" + "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8fa22178dfc368911dbd513b431cd9b06f9afe7a", - "reference": "8fa22178dfc368911dbd513b431cd9b06f9afe7a", + "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", + "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-php80": "^1.16" }, - "time": "2023-12-02T08:41:43+00:00", + "time": "2024-11-06T11:36:42+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -6143,7 +6143,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.34" + "source": "https://github.com/symfony/process/tree/v5.4.47" }, "funding": [ { @@ -6163,17 +6163,17 @@ }, { "name": "symfony/routing", - "version": "v5.4.25", - "version_normalized": "5.4.25.0", + "version": "v5.4.48", + "version_normalized": "5.4.48.0", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "56bfc1394f7011303eb2e22724f9b422d3f14649" + "reference": "dd08c19879a9b37ff14fd30dcbdf99a4cf045db1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/56bfc1394f7011303eb2e22724f9b422d3f14649", - "reference": "56bfc1394f7011303eb2e22724f9b422d3f14649", + "url": "https://api.github.com/repos/symfony/routing/zipball/dd08c19879a9b37ff14fd30dcbdf99a4cf045db1", + "reference": "dd08c19879a9b37ff14fd30dcbdf99a4cf045db1", "shasum": "" }, "require": { @@ -6202,7 +6202,7 @@ "symfony/http-foundation": "For using a Symfony Request object", "symfony/yaml": "For using the YAML loader" }, - "time": "2023-06-05T14:18:47+00:00", + "time": "2024-11-12T18:20:21+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -6236,7 +6236,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.25" + "source": "https://github.com/symfony/routing/tree/v5.4.48" }, "funding": [ { @@ -6429,17 +6429,17 @@ }, { "name": "symfony/translation", - "version": "v5.4.24", - "version_normalized": "5.4.24.0", + "version": "v5.4.45", + "version_normalized": "5.4.45.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "de237e59c5833422342be67402d487fbf50334ff" + "reference": "98f26acc99341ca4bab345fb14d7b1d7cb825bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/de237e59c5833422342be67402d487fbf50334ff", - "reference": "de237e59c5833422342be67402d487fbf50334ff", + "url": "https://api.github.com/repos/symfony/translation/zipball/98f26acc99341ca4bab345fb14d7b1d7cb825bed", + "reference": "98f26acc99341ca4bab345fb14d7b1d7cb825bed", "shasum": "" }, "require": { @@ -6478,7 +6478,7 @@ "symfony/config": "", "symfony/yaml": "" }, - "time": "2023-05-19T12:34:17+00:00", + "time": "2024-09-25T14:11:13+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -6509,7 +6509,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.4.24" + "source": "https://github.com/symfony/translation/tree/v5.4.45" }, "funding": [ { diff --git a/composer/installed.php b/composer/installed.php index 6e516a8b6..84df881f9 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -689,9 +689,9 @@ 'dev_requirement' => false, ), 'symfony/console' => array( - 'pretty_version' => 'v5.4.35', - 'version' => '5.4.35.0', - 'reference' => 'dbdf6adcb88d5f83790e1efb57ef4074309d3931', + 'pretty_version' => 'v5.4.47', + 'version' => '5.4.47.0', + 'reference' => 'c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/console', 'aliases' => array(), @@ -725,9 +725,9 @@ 'dev_requirement' => false, ), 'symfony/event-dispatcher' => array( - 'pretty_version' => 'v5.4.26', - 'version' => '5.4.26.0', - 'reference' => '5dcc00e03413f05c1e7900090927bb7247cb0aac', + 'pretty_version' => 'v5.4.45', + 'version' => '5.4.45.0', + 'reference' => '72982eb416f61003e9bb6e91f8b3213600dcf9e9', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher', 'aliases' => array(), @@ -749,18 +749,18 @@ ), ), 'symfony/http-foundation' => array( - 'pretty_version' => 'v5.4.25', - 'version' => '5.4.25.0', - 'reference' => 'f66be2706075c5f6325d2fe2b743a57fb5d23f6b', + 'pretty_version' => 'v5.4.48', + 'version' => '5.4.48.0', + 'reference' => '3f38b8af283b830e1363acd79e5bc3412d055341', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/http-foundation', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/mailer' => array( - 'pretty_version' => 'v5.4.22', - 'version' => '5.4.22.0', - 'reference' => '6330cd465dfd8b7a07515757a1c37069075f7b0b', + 'pretty_version' => 'v5.4.45', + 'version' => '5.4.45.0', + 'reference' => 'f732e1fafdf0f4a2d865e91f1018aaca174aeed9', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/mailer', 'aliases' => array(), @@ -848,18 +848,18 @@ 'dev_requirement' => false, ), 'symfony/process' => array( - 'pretty_version' => 'v5.4.34', - 'version' => '5.4.34.0', - 'reference' => '8fa22178dfc368911dbd513b431cd9b06f9afe7a', + 'pretty_version' => 'v5.4.47', + 'version' => '5.4.47.0', + 'reference' => '5d1662fb32ebc94f17ddb8d635454a776066733d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/process', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/routing' => array( - 'pretty_version' => 'v5.4.25', - 'version' => '5.4.25.0', - 'reference' => '56bfc1394f7011303eb2e22724f9b422d3f14649', + 'pretty_version' => 'v5.4.48', + 'version' => '5.4.48.0', + 'reference' => 'dd08c19879a9b37ff14fd30dcbdf99a4cf045db1', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/routing', 'aliases' => array(), @@ -884,9 +884,9 @@ 'dev_requirement' => false, ), 'symfony/translation' => array( - 'pretty_version' => 'v5.4.24', - 'version' => '5.4.24.0', - 'reference' => 'de237e59c5833422342be67402d487fbf50334ff', + 'pretty_version' => 'v5.4.45', + 'version' => '5.4.45.0', + 'reference' => '98f26acc99341ca4bab345fb14d7b1d7cb825bed', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/translation', 'aliases' => array(), diff --git a/symfony/console/Application.php b/symfony/console/Application.php index bb5341882..1a7e50388 100644 --- a/symfony/console/Application.php +++ b/symfony/console/Application.php @@ -165,9 +165,9 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu } } - $this->configureIO($input, $output); - try { + $this->configureIO($input, $output); + $exitCode = $this->doRun($input, $output); } catch (\Exception $e) { if (!$this->catchExceptions) { @@ -858,7 +858,7 @@ protected function doRenderThrowable(\Throwable $e, OutputInterface $output): vo } if (str_contains($message, "@anonymous\0")) { - $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)?[0-9a-fA-F]++/', function ($m) { return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; }, $message); } diff --git a/symfony/console/Completion/CompletionInput.php b/symfony/console/Completion/CompletionInput.php index 368b94507..2f631bcd8 100644 --- a/symfony/console/Completion/CompletionInput.php +++ b/symfony/console/Completion/CompletionInput.php @@ -53,7 +53,7 @@ public static function fromString(string $inputStr, int $currentIndex): self * Create an input based on an COMP_WORDS token list. * * @param string[] $tokens the set of split tokens (e.g. COMP_WORDS or argv) - * @param $currentIndex the index of the cursor (e.g. COMP_CWORD) + * @param int $currentIndex the index of the cursor (e.g. COMP_CWORD) */ public static function fromTokens(array $tokens, int $currentIndex): self { diff --git a/symfony/console/Helper/ProgressBar.php b/symfony/console/Helper/ProgressBar.php index 1d7b8d456..6250732eb 100644 --- a/symfony/console/Helper/ProgressBar.php +++ b/symfony/console/Helper/ProgressBar.php @@ -169,9 +169,12 @@ public function setMessage(string $message, string $name = 'message') $this->messages[$name] = $message; } + /** + * @return string|null + */ public function getMessage(string $name = 'message') { - return $this->messages[$name]; + return $this->messages[$name] ?? null; } public function getStartTime(): int diff --git a/symfony/console/Helper/QuestionHelper.php b/symfony/console/Helper/QuestionHelper.php index e236be92a..7b9de9229 100644 --- a/symfony/console/Helper/QuestionHelper.php +++ b/symfony/console/Helper/QuestionHelper.php @@ -503,19 +503,7 @@ private function isInteractiveInput($inputStream): bool return self::$stdinIsInteractive; } - if (\function_exists('stream_isatty')) { - return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r')); - } - - if (\function_exists('posix_isatty')) { - return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); - } - - if (!\function_exists('shell_exec')) { - return self::$stdinIsInteractive = true; - } - - return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); + return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r')); } /** diff --git a/symfony/console/Helper/Table.php b/symfony/console/Helper/Table.php index 408a76d67..698f9693b 100644 --- a/symfony/console/Helper/Table.php +++ b/symfony/console/Helper/Table.php @@ -621,9 +621,10 @@ private function buildTableRows(array $rows): TableRows if (!strstr($cell ?? '', "\n")) { continue; } - $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell))); + $eol = str_contains($cell ?? '', "\r\n") ? "\r\n" : "\n"; + $escaped = implode($eol, array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode($eol, $cell))); $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped; - $lines = explode("\n", str_replace("\n", "\n", $cell)); + $lines = explode($eol, str_replace($eol, ''.$eol, $cell)); foreach ($lines as $lineKey => $line) { if ($colspan > 1) { $line = new TableCell($line, ['colspan' => $colspan]); @@ -685,8 +686,9 @@ private function fillNextRows(array $rows, int $line): array $nbLines = $cell->getRowspan() - 1; $lines = [$cell]; if (strstr($cell, "\n")) { - $lines = explode("\n", str_replace("\n", "\n", $cell)); - $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + $eol = str_contains($cell, "\r\n") ? "\r\n" : "\n"; + $lines = explode($eol, str_replace($eol, ''.$eol.'', $cell)); + $nbLines = \count($lines) > $nbLines ? substr_count($cell, $eol) : $nbLines; $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]); unset($lines[0]); diff --git a/symfony/console/Input/InputOption.php b/symfony/console/Input/InputOption.php index 1d8dbca31..99807f59e 100644 --- a/symfony/console/Input/InputOption.php +++ b/symfony/console/Input/InputOption.php @@ -69,7 +69,7 @@ public function __construct(string $name, $shortcut = null, ?int $mode = null, s throw new InvalidArgumentException('An option name cannot be empty.'); } - if ('' === $shortcut || [] === $shortcut) { + if ('' === $shortcut || [] === $shortcut || false === $shortcut) { $shortcut = null; } diff --git a/symfony/console/Output/StreamOutput.php b/symfony/console/Output/StreamOutput.php index 0ef15cf31..b53955269 100644 --- a/symfony/console/Output/StreamOutput.php +++ b/symfony/console/Output/StreamOutput.php @@ -91,54 +91,33 @@ protected function doWrite(string $message, bool $newline) protected function hasColorSupport() { // Follow https://no-color.org/ - if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { + if ('' !== (($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR'))[0] ?? '')) { return false; } - if (!$this->isTty()) { + // Detect msysgit/mingw and assume this is a tty because detection + // does not work correctly, see https://github.com/composer/composer/issues/9690 + if (!@stream_isatty($this->stream) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) { return false; } - if (\DIRECTORY_SEPARATOR === '\\' - && \function_exists('sapi_windows_vt100_support') - && @sapi_windows_vt100_support($this->stream) - ) { + if ('\\' === \DIRECTORY_SEPARATOR && @sapi_windows_vt100_support($this->stream)) { return true; } - return 'Hyper' === getenv('TERM_PROGRAM') + if ('Hyper' === getenv('TERM_PROGRAM') + || false !== getenv('COLORTERM') || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') - || str_starts_with((string) getenv('TERM'), 'xterm'); - } - - /** - * Checks if the stream is a TTY, i.e; whether the output stream is connected to a terminal. - * - * Reference: Composer\Util\Platform::isTty - * https://github.com/composer/composer - */ - private function isTty(): bool - { - // Detect msysgit/mingw and assume this is a tty because detection - // does not work correctly, see https://github.com/composer/composer/issues/9690 - if (\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) { + ) { return true; } - // Modern cross-platform function, includes the fstat fallback so if it is present we trust it - if (\function_exists('stream_isatty')) { - return stream_isatty($this->stream); - } - - // Only trusting this if it is positive, otherwise prefer fstat fallback. - if (\function_exists('posix_isatty') && posix_isatty($this->stream)) { - return true; + if ('dumb' === $term = (string) getenv('TERM')) { + return false; } - $stat = @fstat($this->stream); - - // Check if formatted mode is S_IFCHR - return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + // See https://github.com/chalk/supports-color/blob/d4f413efaf8da045c5ab440ed418ef02dbb28bf1/index.js#L157 + return preg_match('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/', $term); } } diff --git a/symfony/console/Resources/completion.bash b/symfony/console/Resources/completion.bash index 64b87ccf7..bb44037b0 100644 --- a/symfony/console/Resources/completion.bash +++ b/symfony/console/Resources/completion.bash @@ -7,7 +7,7 @@ _sf_{{ COMMAND_NAME }}() { # Use newline as only separator to allow space in completion values - IFS=$'\n' + local IFS=$'\n' local sf_cmd="${COMP_WORDS[0]}" # for an alias, get the real script behind it diff --git a/symfony/console/Terminal.php b/symfony/console/Terminal.php index b91e8afc5..ee178327a 100644 --- a/symfony/console/Terminal.php +++ b/symfony/console/Terminal.php @@ -158,8 +158,7 @@ private static function readFromProcess(string $command): ?string $cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0; - $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); - if (!\is_resource($process)) { + if (!$process = @proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true])) { return null; } diff --git a/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php index acfbf619c..84d6a08a1 100644 --- a/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php +++ b/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @@ -42,7 +42,7 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa private $requestStack; private $currentRequestHash = ''; - public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null) + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, ?LoggerInterface $logger = null, ?RequestStack $requestStack = null) { $this->dispatcher = $dispatcher; $this->stopwatch = $stopwatch; @@ -97,7 +97,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function getListeners(string $eventName = null) + public function getListeners(?string $eventName = null) { return $this->dispatcher->getListeners($eventName); } @@ -123,7 +123,7 @@ public function getListenerPriority(string $eventName, $listener) /** * {@inheritdoc} */ - public function hasListeners(string $eventName = null) + public function hasListeners(?string $eventName = null) { return $this->dispatcher->hasListeners($eventName); } @@ -131,7 +131,7 @@ public function hasListeners(string $eventName = null) /** * {@inheritdoc} */ - public function dispatch(object $event, string $eventName = null): object + public function dispatch(object $event, ?string $eventName = null): object { $eventName = $eventName ?? \get_class($event); @@ -171,7 +171,7 @@ public function dispatch(object $event, string $eventName = null): object /** * @return array */ - public function getCalledListeners(Request $request = null) + public function getCalledListeners(?Request $request = null) { if (null === $this->callStack) { return []; @@ -192,7 +192,7 @@ public function getCalledListeners(Request $request = null) /** * @return array */ - public function getNotCalledListeners(Request $request = null) + public function getNotCalledListeners(?Request $request = null) { try { $allListeners = $this->getListeners(); @@ -235,7 +235,7 @@ public function getNotCalledListeners(Request $request = null) return $notCalled; } - public function getOrphanedEvents(Request $request = null): array + public function getOrphanedEvents(?Request $request = null): array { if ($request) { return $this->orphanedEvents[spl_object_hash($request)] ?? []; diff --git a/symfony/event-dispatcher/Debug/WrappedListener.php b/symfony/event-dispatcher/Debug/WrappedListener.php index 3c4cc1335..792c17561 100644 --- a/symfony/event-dispatcher/Debug/WrappedListener.php +++ b/symfony/event-dispatcher/Debug/WrappedListener.php @@ -33,7 +33,7 @@ final class WrappedListener private $priority; private static $hasClassStub; - public function __construct($listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) + public function __construct($listener, ?string $name, Stopwatch $stopwatch, ?EventDispatcherInterface $dispatcher = null) { $this->listener = $listener; $this->optimizedListener = $listener instanceof \Closure ? $listener : (\is_callable($listener) ? \Closure::fromCallable($listener) : null); @@ -47,7 +47,7 @@ public function __construct($listener, ?string $name, Stopwatch $stopwatch, Even $this->pretty = $this->name.'::'.$listener[1]; } elseif ($listener instanceof \Closure) { $r = new \ReflectionFunction($listener); - if (str_contains($r->name, '{closure}')) { + if (str_contains($r->name, '{closure')) { $this->pretty = $this->name = 'closure'; } elseif ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $this->name = $class->name; diff --git a/symfony/event-dispatcher/EventDispatcher.php b/symfony/event-dispatcher/EventDispatcher.php index 8fe8fb5c2..9c86bd95c 100644 --- a/symfony/event-dispatcher/EventDispatcher.php +++ b/symfony/event-dispatcher/EventDispatcher.php @@ -45,7 +45,7 @@ public function __construct() /** * {@inheritdoc} */ - public function dispatch(object $event, string $eventName = null): object + public function dispatch(object $event, ?string $eventName = null): object { $eventName = $eventName ?? \get_class($event); @@ -65,7 +65,7 @@ public function dispatch(object $event, string $eventName = null): object /** * {@inheritdoc} */ - public function getListeners(string $eventName = null) + public function getListeners(?string $eventName = null) { if (null !== $eventName) { if (empty($this->listeners[$eventName])) { @@ -120,7 +120,7 @@ public function getListenerPriority(string $eventName, $listener) /** * {@inheritdoc} */ - public function hasListeners(string $eventName = null) + public function hasListeners(?string $eventName = null) { if (null !== $eventName) { return !empty($this->listeners[$eventName]); diff --git a/symfony/event-dispatcher/EventDispatcherInterface.php b/symfony/event-dispatcher/EventDispatcherInterface.php index cc324e1c6..4b65e5a66 100644 --- a/symfony/event-dispatcher/EventDispatcherInterface.php +++ b/symfony/event-dispatcher/EventDispatcherInterface.php @@ -50,7 +50,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber); * * @return array */ - public function getListeners(string $eventName = null); + public function getListeners(?string $eventName = null); /** * Gets the listener priority for a specific event. @@ -66,5 +66,5 @@ public function getListenerPriority(string $eventName, callable $listener); * * @return bool */ - public function hasListeners(string $eventName = null); + public function hasListeners(?string $eventName = null); } diff --git a/symfony/event-dispatcher/GenericEvent.php b/symfony/event-dispatcher/GenericEvent.php index b32a301ae..4ecd29e3b 100644 --- a/symfony/event-dispatcher/GenericEvent.php +++ b/symfony/event-dispatcher/GenericEvent.php @@ -29,7 +29,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate protected $arguments; /** - * Encapsulate an event with $subject and $args. + * Encapsulate an event with $subject and $arguments. * * @param mixed $subject The subject of the event, usually an object or a callable * @param array $arguments Arguments to store in the event diff --git a/symfony/event-dispatcher/ImmutableEventDispatcher.php b/symfony/event-dispatcher/ImmutableEventDispatcher.php index 568d79c3a..4e00bfa45 100644 --- a/symfony/event-dispatcher/ImmutableEventDispatcher.php +++ b/symfony/event-dispatcher/ImmutableEventDispatcher.php @@ -28,7 +28,7 @@ public function __construct(EventDispatcherInterface $dispatcher) /** * {@inheritdoc} */ - public function dispatch(object $event, string $eventName = null): object + public function dispatch(object $event, ?string $eventName = null): object { return $this->dispatcher->dispatch($event, $eventName); } @@ -68,7 +68,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function getListeners(string $eventName = null) + public function getListeners(?string $eventName = null) { return $this->dispatcher->getListeners($eventName); } @@ -84,7 +84,7 @@ public function getListenerPriority(string $eventName, $listener) /** * {@inheritdoc} */ - public function hasListeners(string $eventName = null) + public function hasListeners(?string $eventName = null) { return $this->dispatcher->hasListeners($eventName); } diff --git a/symfony/http-foundation/BinaryFileResponse.php b/symfony/http-foundation/BinaryFileResponse.php index d3caa36aa..ccfd6389a 100644 --- a/symfony/http-foundation/BinaryFileResponse.php +++ b/symfony/http-foundation/BinaryFileResponse.php @@ -45,7 +45,7 @@ class BinaryFileResponse extends Response * @param bool $autoEtag Whether the ETag header should be automatically set * @param bool $autoLastModified Whether the Last-Modified header should be automatically set */ - public function __construct($file, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) + public function __construct($file, int $status = 200, array $headers = [], bool $public = true, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) { parent::__construct(null, $status, $headers); @@ -69,7 +69,7 @@ public function __construct($file, int $status = 200, array $headers = [], bool * * @deprecated since Symfony 5.2, use __construct() instead. */ - public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) + public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) { trigger_deprecation('symfony/http-foundation', '5.2', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class); @@ -85,7 +85,7 @@ public static function create($file = null, int $status = 200, array $headers = * * @throws FileException */ - public function setFile($file, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) + public function setFile($file, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) { if (!$file instanceof File) { if ($file instanceof \SplFileInfo) { @@ -244,8 +244,12 @@ public function prepare(Request $request) } if ('x-accel-redirect' === strtolower($type)) { // Do X-Accel-Mapping substitutions. - // @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect - $parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',='); + // @link https://github.com/rack/rack/blob/main/lib/rack/sendfile.rb + // @link https://mattbrictson.com/blog/accelerated-rails-downloads + if (!$request->headers->has('X-Accel-Mapping')) { + throw new \LogicException('The "X-Accel-Mapping" header must be set when "X-Sendfile-Type" is set to "X-Accel-Redirect".'); + } + $parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping'), ',='); foreach ($parts as $part) { [$pathPrefix, $location] = $part; if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) { diff --git a/symfony/http-foundation/Cookie.php b/symfony/http-foundation/Cookie.php index 91024535b..3ff93b9c1 100644 --- a/symfony/http-foundation/Cookie.php +++ b/symfony/http-foundation/Cookie.php @@ -71,7 +71,7 @@ public static function fromString(string $cookie, bool $decode = false) return new static($name, $value, $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']); } - public static function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self + public static function create(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self { return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite); } @@ -89,7 +89,7 @@ public static function create(string $name, string $value = null, $expire = 0, ? * * @throws \InvalidArgumentException */ - public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = 'lax') + public function __construct(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = 'lax') { // from PHP source code if ($raw && false !== strpbrk($name, self::RESERVED_CHARS_LIST)) { diff --git a/symfony/http-foundation/Exception/SessionNotFoundException.php b/symfony/http-foundation/Exception/SessionNotFoundException.php index 94b0cb69a..80a21bf15 100644 --- a/symfony/http-foundation/Exception/SessionNotFoundException.php +++ b/symfony/http-foundation/Exception/SessionNotFoundException.php @@ -20,7 +20,7 @@ */ class SessionNotFoundException extends \LogicException implements RequestExceptionInterface { - public function __construct(string $message = 'There is currently no session available.', int $code = 0, \Throwable $previous = null) + public function __construct(string $message = 'There is currently no session available.', int $code = 0, ?\Throwable $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/symfony/http-foundation/File/File.php b/symfony/http-foundation/File/File.php index d941577d2..2deb53d6d 100644 --- a/symfony/http-foundation/File/File.php +++ b/symfony/http-foundation/File/File.php @@ -88,7 +88,7 @@ public function getMimeType() * * @throws FileException if the target file could not be created */ - public function move(string $directory, string $name = null) + public function move(string $directory, ?string $name = null) { $target = $this->getTargetFile($directory, $name); @@ -121,7 +121,7 @@ public function getContent(): string /** * @return self */ - protected function getTargetFile(string $directory, string $name = null) + protected function getTargetFile(string $directory, ?string $name = null) { if (!is_dir($directory)) { if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) { diff --git a/symfony/http-foundation/File/UploadedFile.php b/symfony/http-foundation/File/UploadedFile.php index fcc629913..6ff6e51a8 100644 --- a/symfony/http-foundation/File/UploadedFile.php +++ b/symfony/http-foundation/File/UploadedFile.php @@ -60,7 +60,7 @@ class UploadedFile extends File * @throws FileException If file_uploads is disabled * @throws FileNotFoundException If the file does not exist */ - public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false) + public function __construct(string $path, string $originalName, ?string $mimeType = null, ?int $error = null, bool $test = false) { $this->originalName = $this->getName($originalName); $this->mimeType = $mimeType ?: 'application/octet-stream'; @@ -74,7 +74,7 @@ public function __construct(string $path, string $originalName, string $mimeType * Returns the original file name. * * It is extracted from the request from which the file has been uploaded. - * Then it should not be considered as a safe value. + * This should not be considered as a safe value to use for a file name on your servers. * * @return string */ @@ -87,7 +87,7 @@ public function getClientOriginalName() * Returns the original file extension. * * It is extracted from the original file name that was uploaded. - * Then it should not be considered as a safe value. + * This should not be considered as a safe value to use for a file name on your servers. * * @return string */ @@ -172,7 +172,7 @@ public function isValid() * * @throws FileException if, for any reason, the file could not have been moved */ - public function move(string $directory, string $name = null) + public function move(string $directory, ?string $name = null) { if ($this->isValid()) { if ($this->test) { diff --git a/symfony/http-foundation/HeaderBag.php b/symfony/http-foundation/HeaderBag.php index 4683a6840..43d5f6327 100644 --- a/symfony/http-foundation/HeaderBag.php +++ b/symfony/http-foundation/HeaderBag.php @@ -67,7 +67,7 @@ public function __toString() * * @return array>|array */ - public function all(string $key = null) + public function all(?string $key = null) { if (null !== $key) { return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? []; @@ -110,7 +110,7 @@ public function add(array $headers) * * @return string|null */ - public function get(string $key, string $default = null) + public function get(string $key, ?string $default = null) { $headers = $this->all($key); @@ -197,7 +197,7 @@ public function remove(string $key) * * @throws \RuntimeException When the HTTP header is not parseable */ - public function getDate(string $key, \DateTime $default = null) + public function getDate(string $key, ?\DateTime $default = null) { if (null === $value = $this->get($key)) { return $default; diff --git a/symfony/http-foundation/HeaderUtils.php b/symfony/http-foundation/HeaderUtils.php index 46b1e6aed..110896e17 100644 --- a/symfony/http-foundation/HeaderUtils.php +++ b/symfony/http-foundation/HeaderUtils.php @@ -33,17 +33,21 @@ private function __construct() * * Example: * - * HeaderUtils::split("da, en-gb;q=0.8", ",;") + * HeaderUtils::split('da, en-gb;q=0.8', ',;') * // => ['da'], ['en-gb', 'q=0.8']] * * @param string $separators List of characters to split on, ordered by - * precedence, e.g. ",", ";=", or ",;=" + * precedence, e.g. ',', ';=', or ',;=' * * @return array Nested array with as many levels as there are characters in * $separators */ public static function split(string $header, string $separators): array { + if ('' === $separators) { + throw new \InvalidArgumentException('At least one separator must be specified.'); + } + $quotedSeparators = preg_quote($separators, '/'); preg_match_all(' @@ -77,8 +81,8 @@ public static function split(string $header, string $separators): array * * Example: * - * HeaderUtils::combine([["foo", "abc"], ["bar"]]) - * // => ["foo" => "abc", "bar" => true] + * HeaderUtils::combine([['foo', 'abc'], ['bar']]) + * // => ['foo' => 'abc', 'bar' => true] */ public static function combine(array $parts): array { @@ -95,13 +99,13 @@ public static function combine(array $parts): array /** * Joins an associative array into a string for use in an HTTP header. * - * The key and value of each entry are joined with "=", and all entries + * The key and value of each entry are joined with '=', and all entries * are joined with the specified separator and an additional space (for * readability). Values are quoted if necessary. * * Example: * - * HeaderUtils::toString(["foo" => "abc", "bar" => true, "baz" => "a b c"], ",") + * HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',') * // => 'foo=abc, bar, baz="a b c"' */ public static function toString(array $assoc, string $separator): string @@ -252,39 +256,40 @@ public static function parseQuery(string $query, bool $ignoreBrackets = false, s private static function groupParts(array $matches, string $separators, bool $first = true): array { $separator = $separators[0]; - $partSeparators = substr($separators, 1); - + $separators = substr($separators, 1) ?: ''; $i = 0; + + if ('' === $separators && !$first) { + $parts = ['']; + + foreach ($matches as $match) { + if (!$i && isset($match['separator'])) { + $i = 1; + $parts[1] = ''; + } else { + $parts[$i] .= self::unquote($match[0]); + } + } + + return $parts; + } + + $parts = []; $partMatches = []; - $previousMatchWasSeparator = false; + foreach ($matches as $match) { - if (!$first && $previousMatchWasSeparator && isset($match['separator']) && $match['separator'] === $separator) { - $previousMatchWasSeparator = true; - $partMatches[$i][] = $match; - } elseif (isset($match['separator']) && $match['separator'] === $separator) { - $previousMatchWasSeparator = true; + if (($match['separator'] ?? null) === $separator) { ++$i; } else { - $previousMatchWasSeparator = false; $partMatches[$i][] = $match; } } - $parts = []; - if ($partSeparators) { - foreach ($partMatches as $matches) { - $parts[] = self::groupParts($matches, $partSeparators, false); - } - } else { - foreach ($partMatches as $matches) { - $parts[] = self::unquote($matches[0][0]); - } - - if (!$first && 2 < \count($parts)) { - $parts = [ - $parts[0], - implode($separator, \array_slice($parts, 1)), - ]; + foreach ($partMatches as $matches) { + if ('' === $separators && '' !== $unquoted = self::unquote($matches[0][0])) { + $parts[] = $unquoted; + } elseif ($groupedParts = self::groupParts($matches, $separators, false)) { + $parts[] = $groupedParts; } } diff --git a/symfony/http-foundation/InputBag.php b/symfony/http-foundation/InputBag.php index a9d3cd82a..356fbbc6f 100644 --- a/symfony/http-foundation/InputBag.php +++ b/symfony/http-foundation/InputBag.php @@ -45,7 +45,7 @@ public function get(string $key, $default = null) /** * {@inheritdoc} */ - public function all(string $key = null): array + public function all(?string $key = null): array { return parent::all($key); } diff --git a/symfony/http-foundation/JsonResponse.php b/symfony/http-foundation/JsonResponse.php index 501a6387d..51bdf1976 100644 --- a/symfony/http-foundation/JsonResponse.php +++ b/symfony/http-foundation/JsonResponse.php @@ -105,7 +105,7 @@ public static function fromJsonString(string $data, int $status = 200, array $he * * @throws \InvalidArgumentException When the callback name is not valid */ - public function setCallback(string $callback = null) + public function setCallback(?string $callback = null) { if (null !== $callback) { // partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/ diff --git a/symfony/http-foundation/ParameterBag.php b/symfony/http-foundation/ParameterBag.php index e1f89d69e..b542292bc 100644 --- a/symfony/http-foundation/ParameterBag.php +++ b/symfony/http-foundation/ParameterBag.php @@ -39,7 +39,7 @@ public function __construct(array $parameters = []) * * @return array */ - public function all(/* string $key = null */) + public function all(/* ?string $key = null */) { $key = \func_num_args() > 0 ? func_get_arg(0) : null; diff --git a/symfony/http-foundation/RedirectResponse.php b/symfony/http-foundation/RedirectResponse.php index 2103280c6..7b89f0faf 100644 --- a/symfony/http-foundation/RedirectResponse.php +++ b/symfony/http-foundation/RedirectResponse.php @@ -103,6 +103,7 @@ public function setTargetUrl(string $url) ', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8'))); $this->headers->set('Location', $url); + $this->headers->set('Content-Type', 'text/html; charset=utf-8'); return $this; } diff --git a/symfony/http-foundation/Request.php b/symfony/http-foundation/Request.php index 28cebad16..d1103cf8a 100644 --- a/symfony/http-foundation/Request.php +++ b/symfony/http-foundation/Request.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpFoundation; +use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; use Symfony\Component\HttpFoundation\Exception\JsonException; use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; @@ -246,6 +247,9 @@ class Request self::HEADER_X_FORWARDED_PREFIX => 'X_FORWARDED_PREFIX', ]; + /** @var bool */ + private $isIisRewrite = false; + /** * @param array $query The GET parameters * @param array $request The POST parameters @@ -330,6 +334,8 @@ public static function createFromGlobals() * @param string|resource|null $content The raw body data * * @return static + * + * @throws BadRequestException When the URI is invalid */ public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null) { @@ -352,7 +358,20 @@ public static function create(string $uri, string $method = 'GET', array $parame $server['PATH_INFO'] = ''; $server['REQUEST_METHOD'] = strtoupper($method); - $components = parse_url($uri); + if (false === $components = parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) { + throw new BadRequestException('Invalid URI.'); + } + + if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) { + throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.'); + } + if (\strlen($uri) !== strcspn($uri, "\r\n\t")) { + throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.'); + } + if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32)) { + throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.'); + } + if (isset($components['host'])) { $server['SERVER_NAME'] = $components['host']; $server['HTTP_HOST'] = $components['host']; @@ -448,7 +467,7 @@ public static function setFactory(?callable $callable) * * @return static */ - public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + public function duplicate(?array $query = null, ?array $request = null, ?array $attributes = null, ?array $cookies = null, ?array $files = null, ?array $server = null) { $dup = clone $this; if (null !== $query) { @@ -1287,7 +1306,7 @@ public function getMethod() } if (!preg_match('/^[A-Z]++$/D', $method)) { - throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method)); + throw new SuspiciousOperationException('Invalid HTTP method override.'); } return $this->method = $method; @@ -1648,7 +1667,7 @@ public function getPreferredFormat(?string $default = 'html'): ?string * * @return string|null */ - public function getPreferredLanguage(array $locales = null) + public function getPreferredLanguage(?array $locales = null) { $preferredLanguages = $this->getLanguages(); @@ -1805,11 +1824,10 @@ protected function prepareRequestUri() { $requestUri = ''; - if ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) { + if ($this->isIisRewrite() && '' != $this->server->get('UNENCODED_URL')) { // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem) $requestUri = $this->server->get('UNENCODED_URL'); $this->server->remove('UNENCODED_URL'); - $this->server->remove('IIS_WasUrlRewritten'); } elseif ($this->server->has('REQUEST_URI')) { $requestUri = $this->server->get('REQUEST_URI'); @@ -2012,7 +2030,13 @@ private function setPhpDefaultLocale(string $locale): void */ private function getUrlencodedPrefix(string $string, string $prefix): ?string { - if (!str_starts_with(rawurldecode($string), $prefix)) { + if ($this->isIisRewrite()) { + // ISS with UrlRewriteModule might report SCRIPT_NAME/PHP_SELF with wrong case + // see https://github.com/php/php-src/issues/11981 + if (0 !== stripos(rawurldecode($string), $prefix)) { + return null; + } + } elseif (!str_starts_with(rawurldecode($string), $prefix)) { return null; } @@ -2053,7 +2077,7 @@ public function isFromTrustedProxy() return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR', ''), self::$trustedProxies); } - private function getTrustedValues(int $type, string $ip = null): array + private function getTrustedValues(int $type, ?string $ip = null): array { $clientValues = []; $forwardedValues = []; @@ -2145,4 +2169,20 @@ private function normalizeAndFilterClientIps(array $clientIps, string $ip): arra // Now the IP chain contains only untrusted proxies and the client IP return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp]; } + + /** + * Is this IIS with UrlRewriteModule? + * + * This method consumes, caches and removed the IIS_WasUrlRewritten env var, + * so we don't inherit it to sub-requests. + */ + private function isIisRewrite(): bool + { + if (1 === $this->server->getInt('IIS_WasUrlRewritten')) { + $this->isIisRewrite = true; + $this->server->remove('IIS_WasUrlRewritten'); + } + + return $this->isIisRewrite; + } } diff --git a/symfony/http-foundation/RequestMatcher.php b/symfony/http-foundation/RequestMatcher.php index f2645f9ae..03ccee97e 100644 --- a/symfony/http-foundation/RequestMatcher.php +++ b/symfony/http-foundation/RequestMatcher.php @@ -58,7 +58,7 @@ class RequestMatcher implements RequestMatcherInterface * @param string|string[]|null $ips * @param string|string[]|null $schemes */ - public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, int $port = null) + public function __construct(?string $path = null, ?string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, ?int $port = null) { $this->matchPath($path); $this->matchHost($host); @@ -91,7 +91,7 @@ public function matchHost(?string $regexp) } /** - * Adds a check for the the URL port. + * Adds a check for the URL port. * * @param int|null $port The port number to connect to */ diff --git a/symfony/http-foundation/Response.php b/symfony/http-foundation/Response.php index 59e974d62..6798a04c8 100644 --- a/symfony/http-foundation/Response.php +++ b/symfony/http-foundation/Response.php @@ -298,7 +298,7 @@ public function prepare(Request $request) $charset = $this->charset ?: 'UTF-8'; if (!$headers->has('Content-Type')) { $headers->set('Content-Type', 'text/html; charset='.$charset); - } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) { + } elseif (0 === stripos($headers->get('Content-Type') ?? '', 'text/') && false === stripos($headers->get('Content-Type') ?? '', 'charset')) { // add the charset $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset); } @@ -463,7 +463,7 @@ public function getProtocolVersion(): string * * @final */ - public function setStatusCode(int $code, string $text = null): object + public function setStatusCode(int $code, ?string $text = null): object { $this->statusCode = $code; if ($this->isInvalid()) { @@ -737,7 +737,7 @@ public function getExpires(): ?\DateTimeInterface * * @final */ - public function setExpires(\DateTimeInterface $date = null): object + public function setExpires(?\DateTimeInterface $date = null): object { if (null === $date) { $this->headers->remove('Expires'); @@ -886,7 +886,7 @@ public function getLastModified(): ?\DateTimeInterface * * @final */ - public function setLastModified(\DateTimeInterface $date = null): object + public function setLastModified(?\DateTimeInterface $date = null): object { if (null === $date) { $this->headers->remove('Last-Modified'); @@ -924,7 +924,7 @@ public function getEtag(): ?string * * @final */ - public function setEtag(string $etag = null, bool $weak = false): object + public function setEtag(?string $etag = null, bool $weak = false): object { if (null === $etag) { $this->headers->remove('Etag'); @@ -1217,7 +1217,7 @@ public function isNotFound(): bool * * @final */ - public function isRedirect(string $location = null): bool + public function isRedirect(?string $location = null): bool { return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location')); } diff --git a/symfony/http-foundation/ResponseHeaderBag.php b/symfony/http-foundation/ResponseHeaderBag.php index 1df13fa21..d4c4f393f 100644 --- a/symfony/http-foundation/ResponseHeaderBag.php +++ b/symfony/http-foundation/ResponseHeaderBag.php @@ -88,7 +88,7 @@ public function replace(array $headers = []) /** * {@inheritdoc} */ - public function all(string $key = null) + public function all(?string $key = null) { $headers = parent::all(); @@ -186,7 +186,7 @@ public function setCookie(Cookie $cookie) /** * Removes a cookie from the array, but does not unset it in the browser. */ - public function removeCookie(string $name, ?string $path = '/', string $domain = null) + public function removeCookie(string $name, ?string $path = '/', ?string $domain = null) { if (null === $path) { $path = '/'; @@ -239,7 +239,7 @@ public function getCookies(string $format = self::COOKIES_FLAT) /** * Clears a cookie in the browser. */ - public function clearCookie(string $name, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, string $sameSite = null) + public function clearCookie(string $name, ?string $path = '/', ?string $domain = null, bool $secure = false, bool $httpOnly = true, ?string $sameSite = null) { $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite)); } diff --git a/symfony/http-foundation/ServerBag.php b/symfony/http-foundation/ServerBag.php index 25688d523..831caa67e 100644 --- a/symfony/http-foundation/ServerBag.php +++ b/symfony/http-foundation/ServerBag.php @@ -31,7 +31,7 @@ public function getHeaders() foreach ($this->parameters as $key => $value) { if (str_starts_with($key, 'HTTP_')) { $headers[substr($key, 5)] = $value; - } elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) { + } elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true) && '' !== $value) { $headers[$key] = $value; } } @@ -51,7 +51,7 @@ public function getHeaders() * RewriteCond %{HTTP:Authorization} .+ * RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0] * RewriteCond %{REQUEST_FILENAME} !-f - * RewriteRule ^(.*)$ app.php [QSA,L] + * RewriteRule ^(.*)$ index.php [QSA,L] */ $authorizationHeader = null; diff --git a/symfony/http-foundation/Session/Session.php b/symfony/http-foundation/Session/Session.php index 022e3986f..917920a46 100644 --- a/symfony/http-foundation/Session/Session.php +++ b/symfony/http-foundation/Session/Session.php @@ -39,7 +39,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable private $usageIndex = 0; private $usageReporter; - public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null, callable $usageReporter = null) + public function __construct(?SessionStorageInterface $storage = null, ?AttributeBagInterface $attributes = null, ?FlashBagInterface $flashes = null, ?callable $usageReporter = null) { $this->storage = $storage ?? new NativeSessionStorage(); $this->usageReporter = $usageReporter; @@ -175,7 +175,7 @@ public function isEmpty(): bool /** * {@inheritdoc} */ - public function invalidate(int $lifetime = null) + public function invalidate(?int $lifetime = null) { $this->storage->clear(); @@ -185,7 +185,7 @@ public function invalidate(int $lifetime = null) /** * {@inheritdoc} */ - public function migrate(bool $destroy = false, int $lifetime = null) + public function migrate(bool $destroy = false, ?int $lifetime = null) { return $this->storage->regenerate($destroy, $lifetime); } diff --git a/symfony/http-foundation/Session/SessionFactory.php b/symfony/http-foundation/Session/SessionFactory.php index 04c4b06a0..bd79282ee 100644 --- a/symfony/http-foundation/Session/SessionFactory.php +++ b/symfony/http-foundation/Session/SessionFactory.php @@ -26,7 +26,7 @@ class SessionFactory implements SessionFactoryInterface private $storageFactory; private $usageReporter; - public function __construct(RequestStack $requestStack, SessionStorageFactoryInterface $storageFactory, callable $usageReporter = null) + public function __construct(RequestStack $requestStack, SessionStorageFactoryInterface $storageFactory, ?callable $usageReporter = null) { $this->requestStack = $requestStack; $this->storageFactory = $storageFactory; diff --git a/symfony/http-foundation/Session/SessionInterface.php b/symfony/http-foundation/Session/SessionInterface.php index e67338337..b73dfd0c3 100644 --- a/symfony/http-foundation/Session/SessionInterface.php +++ b/symfony/http-foundation/Session/SessionInterface.php @@ -66,7 +66,7 @@ public function setName(string $name); * * @return bool */ - public function invalidate(int $lifetime = null); + public function invalidate(?int $lifetime = null); /** * Migrates the current session to a new session id while maintaining all @@ -80,7 +80,7 @@ public function invalidate(int $lifetime = null); * * @return bool */ - public function migrate(bool $destroy = false, int $lifetime = null); + public function migrate(bool $destroy = false, ?int $lifetime = null); /** * Force the session to be saved and closed. diff --git a/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php index 52a103879..570d4f427 100644 --- a/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php +++ b/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php @@ -28,7 +28,7 @@ class NativeFileSessionHandler extends \SessionHandler * @throws \InvalidArgumentException On invalid $savePath * @throws \RuntimeException When failing to create the save directory */ - public function __construct(string $savePath = null) + public function __construct(?string $savePath = null) { if (null === $savePath) { $savePath = \ini_get('session.save_path'); diff --git a/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php index cad7e0a72..f9c5d9b59 100644 --- a/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php @@ -112,16 +112,16 @@ class PdoSessionHandler extends AbstractSessionHandler /** * Username when lazy-connect. * - * @var string + * @var string|null */ - private $username = ''; + private $username = null; /** * Password when lazy-connect. * - * @var string + * @var string|null */ - private $password = ''; + private $password = null; /** * Connection options when lazy-connect. diff --git a/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php b/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php index 39dc30c6f..76e4373f8 100644 --- a/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php +++ b/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php @@ -63,7 +63,7 @@ public static function createHandler($connection): AbstractSessionHandler case str_starts_with($connection, 'rediss:'): case str_starts_with($connection, 'memcached:'): if (!class_exists(AbstractAdapter::class)) { - throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $connection)); + throw new \InvalidArgumentException('Unsupported Redis or Memcached DSN. Try running "composer require symfony/cache".'); } $handlerClass = str_starts_with($connection, 'memcached:') ? MemcachedSessionHandler::class : RedisSessionHandler::class; $connection = AbstractAdapter::createConnection($connection, ['lazy' => true]); @@ -72,7 +72,7 @@ public static function createHandler($connection): AbstractSessionHandler case str_starts_with($connection, 'pdo_oci://'): if (!class_exists(DriverManager::class)) { - throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection)); + throw new \InvalidArgumentException('Unsupported PDO OCI DSN. Try running "composer require doctrine/dbal".'); } $connection[3] = '-'; $params = class_exists(DsnParser::class) ? (new DsnParser())->parse($connection) : ['url' => $connection]; @@ -82,6 +82,7 @@ public static function createHandler($connection): AbstractSessionHandler } $connection = DriverManager::getConnection($params, $config); + // The condition should be removed once support for DBAL <3.3 is dropped $connection = method_exists($connection, 'getNativeConnection') ? $connection->getNativeConnection() : $connection->getWrappedConnection(); // no break; diff --git a/symfony/http-foundation/Session/Storage/MetadataBag.php b/symfony/http-foundation/Session/Storage/MetadataBag.php index 52d332094..3e10f6dbc 100644 --- a/symfony/http-foundation/Session/Storage/MetadataBag.php +++ b/symfony/http-foundation/Session/Storage/MetadataBag.php @@ -100,7 +100,7 @@ public function getLifetime() * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. */ - public function stampNew(int $lifetime = null) + public function stampNew(?int $lifetime = null) { $this->stampCreated($lifetime); } @@ -158,7 +158,7 @@ public function setName(string $name) $this->name = $name; } - private function stampCreated(int $lifetime = null): void + private function stampCreated(?int $lifetime = null): void { $timeStamp = time(); $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp; diff --git a/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php index c5c2bb073..c6a28b1a4 100644 --- a/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php +++ b/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php @@ -62,7 +62,7 @@ class MockArraySessionStorage implements SessionStorageInterface */ protected $bags = []; - public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null) + public function __construct(string $name = 'MOCKSESSID', ?MetadataBag $metaBag = null) { $this->name = $name; $this->setMetadataBag($metaBag); @@ -94,7 +94,7 @@ public function start() /** * {@inheritdoc} */ - public function regenerate(bool $destroy = false, int $lifetime = null) + public function regenerate(bool $destroy = false, ?int $lifetime = null) { if (!$this->started) { $this->start(); @@ -204,7 +204,7 @@ public function isStarted() return $this->started; } - public function setMetadataBag(MetadataBag $bag = null) + public function setMetadataBag(?MetadataBag $bag = null) { if (null === $bag) { $bag = new MetadataBag(); @@ -226,14 +226,11 @@ public function getMetadataBag() /** * Generates a session ID. * - * This doesn't need to be particularly cryptographically secure since this is just - * a mock. - * * @return string */ protected function generateId() { - return hash('sha256', uniqid('ss_mock_', true)); + return bin2hex(random_bytes(16)); } protected function loadSession() diff --git a/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php index 8e32a45e3..8aeb9724c 100644 --- a/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php +++ b/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php @@ -30,7 +30,7 @@ class MockFileSessionStorage extends MockArraySessionStorage /** * @param string|null $savePath Path of directory to save session files */ - public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null) + public function __construct(?string $savePath = null, string $name = 'MOCKSESSID', ?MetadataBag $metaBag = null) { if (null === $savePath) { $savePath = sys_get_temp_dir(); @@ -68,7 +68,7 @@ public function start() /** * {@inheritdoc} */ - public function regenerate(bool $destroy = false, int $lifetime = null) + public function regenerate(bool $destroy = false, ?int $lifetime = null) { if (!$this->started) { $this->start(); diff --git a/symfony/http-foundation/Session/Storage/MockFileSessionStorageFactory.php b/symfony/http-foundation/Session/Storage/MockFileSessionStorageFactory.php index d0da1e169..900fa7cfa 100644 --- a/symfony/http-foundation/Session/Storage/MockFileSessionStorageFactory.php +++ b/symfony/http-foundation/Session/Storage/MockFileSessionStorageFactory.php @@ -28,7 +28,7 @@ class MockFileSessionStorageFactory implements SessionStorageFactoryInterface /** * @see MockFileSessionStorage constructor. */ - public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null) + public function __construct(?string $savePath = null, string $name = 'MOCKSESSID', ?MetadataBag $metaBag = null) { $this->savePath = $savePath; $this->name = $name; diff --git a/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/symfony/http-foundation/Session/Storage/NativeSessionStorage.php index 242478c42..e7b42ed0b 100644 --- a/symfony/http-foundation/Session/Storage/NativeSessionStorage.php +++ b/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @@ -97,7 +97,7 @@ class NativeSessionStorage implements SessionStorageInterface * * @param AbstractProxy|\SessionHandlerInterface|null $handler */ - public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null) + public function __construct(array $options = [], $handler = null, ?MetadataBag $metaBag = null) { if (!\extension_loaded('session')) { throw new \LogicException('PHP extension "session" is required.'); @@ -233,7 +233,7 @@ public function setName(string $name) /** * {@inheritdoc} */ - public function regenerate(bool $destroy = false, int $lifetime = null) + public function regenerate(bool $destroy = false, ?int $lifetime = null) { // Cannot regenerate the session ID for non-active sessions. if (\PHP_SESSION_ACTIVE !== session_status()) { @@ -355,7 +355,7 @@ public function getBag(string $name) return $this->bags[$name]; } - public function setMetadataBag(MetadataBag $metaBag = null) + public function setMetadataBag(?MetadataBag $metaBag = null) { if (null === $metaBag) { $metaBag = new MetadataBag(); @@ -487,7 +487,7 @@ public function setSaveHandler($saveHandler = null) * PHP takes the return value from the read() handler, unserializes it * and populates $_SESSION with the result automatically. */ - protected function loadSession(array &$session = null) + protected function loadSession(?array &$session = null) { if (null === $session) { $session = &$_SESSION; diff --git a/symfony/http-foundation/Session/Storage/NativeSessionStorageFactory.php b/symfony/http-foundation/Session/Storage/NativeSessionStorageFactory.php index a7d7411ff..48e65267e 100644 --- a/symfony/http-foundation/Session/Storage/NativeSessionStorageFactory.php +++ b/symfony/http-foundation/Session/Storage/NativeSessionStorageFactory.php @@ -29,7 +29,7 @@ class NativeSessionStorageFactory implements SessionStorageFactoryInterface /** * @see NativeSessionStorage constructor. */ - public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null, bool $secure = false) + public function __construct(array $options = [], $handler = null, ?MetadataBag $metaBag = null, bool $secure = false) { $this->options = $options; $this->handler = $handler; diff --git a/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php index 72dbef134..855d5e111 100644 --- a/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php +++ b/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php @@ -23,7 +23,7 @@ class PhpBridgeSessionStorage extends NativeSessionStorage /** * @param AbstractProxy|\SessionHandlerInterface|null $handler */ - public function __construct($handler = null, MetadataBag $metaBag = null) + public function __construct($handler = null, ?MetadataBag $metaBag = null) { if (!\extension_loaded('session')) { throw new \LogicException('PHP extension "session" is required.'); diff --git a/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorageFactory.php b/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorageFactory.php index 173ef71de..aa9326322 100644 --- a/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorageFactory.php +++ b/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorageFactory.php @@ -28,7 +28,7 @@ class PhpBridgeSessionStorageFactory implements SessionStorageFactoryInterface /** * @see PhpBridgeSessionStorage constructor. */ - public function __construct($handler = null, MetadataBag $metaBag = null, bool $secure = false) + public function __construct($handler = null, ?MetadataBag $metaBag = null, bool $secure = false) { $this->handler = $handler; $this->metaBag = $metaBag; diff --git a/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/symfony/http-foundation/Session/Storage/SessionStorageInterface.php index 705374552..70b7c6a15 100644 --- a/symfony/http-foundation/Session/Storage/SessionStorageInterface.php +++ b/symfony/http-foundation/Session/Storage/SessionStorageInterface.php @@ -90,7 +90,7 @@ public function setName(string $name); * * @throws \RuntimeException If an error occurs while regenerating this storage */ - public function regenerate(bool $destroy = false, int $lifetime = null); + public function regenerate(bool $destroy = false, ?int $lifetime = null); /** * Force the session to be saved and closed. diff --git a/symfony/http-foundation/StreamedResponse.php b/symfony/http-foundation/StreamedResponse.php index 0599bd1e4..b42330dcd 100644 --- a/symfony/http-foundation/StreamedResponse.php +++ b/symfony/http-foundation/StreamedResponse.php @@ -30,7 +30,7 @@ class StreamedResponse extends Response protected $streamed; private $headersSent; - public function __construct(callable $callback = null, int $status = 200, array $headers = []) + public function __construct(?callable $callback = null, int $status = 200, array $headers = []) { parent::__construct(null, $status, $headers); diff --git a/symfony/mailer/DataCollector/MessageDataCollector.php b/symfony/mailer/DataCollector/MessageDataCollector.php index 07f77b27b..ba94d9b4e 100644 --- a/symfony/mailer/DataCollector/MessageDataCollector.php +++ b/symfony/mailer/DataCollector/MessageDataCollector.php @@ -32,7 +32,7 @@ public function __construct(MessageLoggerListener $logger) /** * {@inheritdoc} */ - public function collect(Request $request, Response $response, \Throwable $exception = null) + public function collect(Request $request, Response $response, ?\Throwable $exception = null) { $this->data['events'] = $this->events; } diff --git a/symfony/mailer/Event/MessageEvents.php b/symfony/mailer/Event/MessageEvents.php index b5266493c..b6b89b39e 100644 --- a/symfony/mailer/Event/MessageEvents.php +++ b/symfony/mailer/Event/MessageEvents.php @@ -35,7 +35,7 @@ public function getTransports(): array /** * @return MessageEvent[] */ - public function getEvents(string $name = null): array + public function getEvents(?string $name = null): array { if (null === $name) { return $this->events; @@ -54,7 +54,7 @@ public function getEvents(string $name = null): array /** * @return RawMessage[] */ - public function getMessages(string $name = null): array + public function getMessages(?string $name = null): array { $events = $this->getEvents($name); $messages = []; diff --git a/symfony/mailer/EventListener/EnvelopeListener.php b/symfony/mailer/EventListener/EnvelopeListener.php index b2980bc5c..db9c0a4e8 100644 --- a/symfony/mailer/EventListener/EnvelopeListener.php +++ b/symfony/mailer/EventListener/EnvelopeListener.php @@ -30,7 +30,7 @@ class EnvelopeListener implements EventSubscriberInterface * @param Address|string $sender * @param array $recipients */ - public function __construct($sender = null, array $recipients = null) + public function __construct($sender = null, ?array $recipients = null) { if (null !== $sender) { $this->sender = Address::create($sender); diff --git a/symfony/mailer/EventListener/MessageListener.php b/symfony/mailer/EventListener/MessageListener.php index f23c69d91..b654bea6e 100644 --- a/symfony/mailer/EventListener/MessageListener.php +++ b/symfony/mailer/EventListener/MessageListener.php @@ -43,7 +43,7 @@ class MessageListener implements EventSubscriberInterface private $headerRules = []; private $renderer; - public function __construct(Headers $headers = null, BodyRendererInterface $renderer = null, array $headerRules = self::DEFAULT_RULES) + public function __construct(?Headers $headers = null, ?BodyRendererInterface $renderer = null, array $headerRules = self::DEFAULT_RULES) { $this->headers = $headers; $this->renderer = $renderer; diff --git a/symfony/mailer/Exception/HttpTransportException.php b/symfony/mailer/Exception/HttpTransportException.php index c72eb6cf6..0ba35eec3 100644 --- a/symfony/mailer/Exception/HttpTransportException.php +++ b/symfony/mailer/Exception/HttpTransportException.php @@ -20,7 +20,7 @@ class HttpTransportException extends TransportException { private $response; - public function __construct(?string $message, ResponseInterface $response, int $code = 0, \Throwable $previous = null) + public function __construct(?string $message, ResponseInterface $response, int $code = 0, ?\Throwable $previous = null) { if (null === $message) { trigger_deprecation('symfony/mailer', '5.3', 'Passing null as $message to "%s()" is deprecated, pass an empty string instead.', __METHOD__); diff --git a/symfony/mailer/Exception/UnsupportedSchemeException.php b/symfony/mailer/Exception/UnsupportedSchemeException.php index e47a129dc..46acf83ef 100644 --- a/symfony/mailer/Exception/UnsupportedSchemeException.php +++ b/symfony/mailer/Exception/UnsupportedSchemeException.php @@ -58,7 +58,7 @@ class UnsupportedSchemeException extends LogicException ], ]; - public function __construct(Dsn $dsn, string $name = null, array $supported = []) + public function __construct(Dsn $dsn, ?string $name = null, array $supported = []) { $provider = $dsn->getScheme(); if (false !== $pos = strpos($provider, '+')) { diff --git a/symfony/mailer/Mailer.php b/symfony/mailer/Mailer.php index cbdcdf296..f4e7f8c9b 100644 --- a/symfony/mailer/Mailer.php +++ b/symfony/mailer/Mailer.php @@ -32,14 +32,14 @@ final class Mailer implements MailerInterface private $bus; private $dispatcher; - public function __construct(TransportInterface $transport, MessageBusInterface $bus = null, EventDispatcherInterface $dispatcher = null) + public function __construct(TransportInterface $transport, ?MessageBusInterface $bus = null, ?EventDispatcherInterface $dispatcher = null) { $this->transport = $transport; $this->bus = $bus; $this->dispatcher = class_exists(Event::class) && $dispatcher instanceof SymfonyEventDispatcherInterface ? LegacyEventDispatcherProxy::decorate($dispatcher) : $dispatcher; } - public function send(RawMessage $message, Envelope $envelope = null): void + public function send(RawMessage $message, ?Envelope $envelope = null): void { if (null === $this->bus) { $this->transport->send($message, $envelope); diff --git a/symfony/mailer/MailerInterface.php b/symfony/mailer/MailerInterface.php index eb44cf640..ebac4b53e 100644 --- a/symfony/mailer/MailerInterface.php +++ b/symfony/mailer/MailerInterface.php @@ -15,7 +15,7 @@ use Symfony\Component\Mime\RawMessage; /** - * Interface for mailers able to send emails synchronous and/or asynchronous. + * Interface for mailers able to send emails synchronously and/or asynchronously. * * Implementations must support synchronous and asynchronous sending. * @@ -26,5 +26,5 @@ interface MailerInterface /** * @throws TransportExceptionInterface */ - public function send(RawMessage $message, Envelope $envelope = null): void; + public function send(RawMessage $message, ?Envelope $envelope = null): void; } diff --git a/symfony/mailer/Messenger/SendEmailMessage.php b/symfony/mailer/Messenger/SendEmailMessage.php index b06ac839c..622408a02 100644 --- a/symfony/mailer/Messenger/SendEmailMessage.php +++ b/symfony/mailer/Messenger/SendEmailMessage.php @@ -22,7 +22,7 @@ class SendEmailMessage private $message; private $envelope; - public function __construct(RawMessage $message, Envelope $envelope = null) + public function __construct(RawMessage $message, ?Envelope $envelope = null) { $this->message = $message; $this->envelope = $envelope; diff --git a/symfony/mailer/Transport.php b/symfony/mailer/Transport.php index c2b813f94..294442e92 100644 --- a/symfony/mailer/Transport.php +++ b/symfony/mailer/Transport.php @@ -63,7 +63,7 @@ class Transport * @param HttpClientInterface|null $client * @param LoggerInterface|null $logger */ - public static function fromDsn(string $dsn/* , EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null */): TransportInterface + public static function fromDsn(string $dsn/* , ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null */): TransportInterface { $dispatcher = 2 <= \func_num_args() ? func_get_arg(1) : null; $client = 3 <= \func_num_args() ? func_get_arg(2) : null; @@ -79,7 +79,7 @@ public static function fromDsn(string $dsn/* , EventDispatcherInterface $dispatc * @param HttpClientInterface|null $client * @param LoggerInterface|null $logger */ - public static function fromDsns(array $dsns/* , EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null */): TransportInterface + public static function fromDsns(array $dsns/* , ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null */): TransportInterface { $dispatcher = 2 <= \func_num_args() ? func_get_arg(1) : null; $client = 3 <= \func_num_args() ? func_get_arg(2) : null; @@ -112,7 +112,7 @@ public function fromString(string $dsn): TransportInterface { [$transport, $offset] = $this->parseDsn($dsn); if ($offset !== \strlen($dsn)) { - throw new InvalidArgumentException(sprintf('The DSN has some garbage at the end: "%s".', substr($dsn, $offset))); + throw new InvalidArgumentException('The mailer DSN has some garbage at the end.'); } return $transport; @@ -183,7 +183,7 @@ public function fromDsnObject(Dsn $dsn): TransportInterface * * @return \Traversable */ - public static function getDefaultFactories(/* EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null */): iterable + public static function getDefaultFactories(/* ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null */): iterable { $dispatcher = 1 <= \func_num_args() ? func_get_arg(0) : null; $client = 2 <= \func_num_args() ? func_get_arg(1) : null; diff --git a/symfony/mailer/Transport/AbstractHttpTransport.php b/symfony/mailer/Transport/AbstractHttpTransport.php index 2317a0da5..47e73c9e5 100644 --- a/symfony/mailer/Transport/AbstractHttpTransport.php +++ b/symfony/mailer/Transport/AbstractHttpTransport.php @@ -28,7 +28,7 @@ abstract class AbstractHttpTransport extends AbstractTransport protected $port; protected $client; - public function __construct(HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + public function __construct(?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) { $this->client = $client; if (null === $client) { diff --git a/symfony/mailer/Transport/AbstractTransport.php b/symfony/mailer/Transport/AbstractTransport.php index 85f04a8b8..f7fd40990 100644 --- a/symfony/mailer/Transport/AbstractTransport.php +++ b/symfony/mailer/Transport/AbstractTransport.php @@ -33,7 +33,7 @@ abstract class AbstractTransport implements TransportInterface private $rate = 0; private $lastSent = 0; - public function __construct(EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + public function __construct(?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) { $this->dispatcher = class_exists(Event::class) && $dispatcher instanceof SymfonyEventDispatcherInterface ? LegacyEventDispatcherProxy::decorate($dispatcher) : $dispatcher; $this->logger = $logger ?? new NullLogger(); @@ -56,7 +56,7 @@ public function setMaxPerSecond(float $rate): self return $this; } - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { $message = clone $message; $envelope = null !== $envelope ? clone $envelope : Envelope::create($message); @@ -104,7 +104,7 @@ private function checkThrottling() $sleep = (1 / $this->rate) - (microtime(true) - $this->lastSent); if (0 < $sleep) { $this->logger->debug(sprintf('Email transport "%s" sleeps for %.2f seconds', __CLASS__, $sleep)); - usleep($sleep * 1000000); + usleep((int) ($sleep * 1000000)); } $this->lastSent = microtime(true); } diff --git a/symfony/mailer/Transport/AbstractTransportFactory.php b/symfony/mailer/Transport/AbstractTransportFactory.php index e1617d270..1f47344f1 100644 --- a/symfony/mailer/Transport/AbstractTransportFactory.php +++ b/symfony/mailer/Transport/AbstractTransportFactory.php @@ -25,7 +25,7 @@ abstract class AbstractTransportFactory implements TransportFactoryInterface protected $client; protected $logger; - public function __construct(EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null) + public function __construct(?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null) { $this->dispatcher = $dispatcher; $this->client = $client; diff --git a/symfony/mailer/Transport/Dsn.php b/symfony/mailer/Transport/Dsn.php index 04d3540f7..8272be713 100644 --- a/symfony/mailer/Transport/Dsn.php +++ b/symfony/mailer/Transport/Dsn.php @@ -25,7 +25,7 @@ final class Dsn private $port; private $options; - public function __construct(string $scheme, string $host, string $user = null, string $password = null, int $port = null, array $options = []) + public function __construct(string $scheme, string $host, ?string $user = null, ?string $password = null, ?int $port = null, array $options = []) { $this->scheme = $scheme; $this->host = $host; @@ -37,24 +37,24 @@ public function __construct(string $scheme, string $host, string $user = null, s public static function fromString(string $dsn): self { - if (false === $parsedDsn = parse_url($dsn)) { - throw new InvalidArgumentException(sprintf('The "%s" mailer DSN is invalid.', $dsn)); + if (false === $params = parse_url($dsn)) { + throw new InvalidArgumentException('The mailer DSN is invalid.'); } - if (!isset($parsedDsn['scheme'])) { - throw new InvalidArgumentException(sprintf('The "%s" mailer DSN must contain a scheme.', $dsn)); + if (!isset($params['scheme'])) { + throw new InvalidArgumentException('The mailer DSN must contain a scheme.'); } - if (!isset($parsedDsn['host'])) { - throw new InvalidArgumentException(sprintf('The "%s" mailer DSN must contain a host (use "default" by default).', $dsn)); + if (!isset($params['host'])) { + throw new InvalidArgumentException('The mailer DSN must contain a host (use "default" by default).'); } - $user = '' !== ($parsedDsn['user'] ?? '') ? urldecode($parsedDsn['user']) : null; - $password = '' !== ($parsedDsn['pass'] ?? '') ? urldecode($parsedDsn['pass']) : null; - $port = $parsedDsn['port'] ?? null; - parse_str($parsedDsn['query'] ?? '', $query); + $user = '' !== ($params['user'] ?? '') ? rawurldecode($params['user']) : null; + $password = '' !== ($params['pass'] ?? '') ? rawurldecode($params['pass']) : null; + $port = $params['port'] ?? null; + parse_str($params['query'] ?? '', $query); - return new self($parsedDsn['scheme'], $parsedDsn['host'], $user, $password, $port, $query); + return new self($params['scheme'], $params['host'], $user, $password, $port, $query); } public function getScheme(): string @@ -77,7 +77,7 @@ public function getPassword(): ?string return $this->password; } - public function getPort(int $default = null): ?int + public function getPort(?int $default = null): ?int { return $this->port ?? $default; } diff --git a/symfony/mailer/Transport/RoundRobinTransport.php b/symfony/mailer/Transport/RoundRobinTransport.php index 761b57f18..2568e4850 100644 --- a/symfony/mailer/Transport/RoundRobinTransport.php +++ b/symfony/mailer/Transport/RoundRobinTransport.php @@ -46,7 +46,7 @@ public function __construct(array $transports, int $retryPeriod = 60) $this->retryPeriod = $retryPeriod; } - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { $exception = null; diff --git a/symfony/mailer/Transport/SendmailTransport.php b/symfony/mailer/Transport/SendmailTransport.php index c60f9218c..712016b5f 100644 --- a/symfony/mailer/Transport/SendmailTransport.php +++ b/symfony/mailer/Transport/SendmailTransport.php @@ -49,7 +49,7 @@ class SendmailTransport extends AbstractTransport * * -f flag will be appended automatically if one is not present. */ - public function __construct(string $command = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + public function __construct(?string $command = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) { parent::__construct($dispatcher, $logger); @@ -64,11 +64,12 @@ public function __construct(string $command = null, EventDispatcherInterface $di $this->stream = new ProcessStream(); if (str_contains($this->command, ' -bs')) { $this->stream->setCommand($this->command); + $this->stream->setInteractive(true); $this->transport = new SmtpTransport($this->stream, $dispatcher, $logger); } } - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { if ($this->transport) { return $this->transport->send($message, $envelope); diff --git a/symfony/mailer/Transport/Smtp/EsmtpTransport.php b/symfony/mailer/Transport/Smtp/EsmtpTransport.php index 1dcb53f15..a223205a3 100644 --- a/symfony/mailer/Transport/Smtp/EsmtpTransport.php +++ b/symfony/mailer/Transport/Smtp/EsmtpTransport.php @@ -30,7 +30,7 @@ class EsmtpTransport extends SmtpTransport private $username = ''; private $password = ''; - public function __construct(string $host = 'localhost', int $port = 0, bool $tls = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + public function __construct(string $host = 'localhost', int $port = 0, ?bool $tls = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) { parent::__construct(null, $dispatcher, $logger); diff --git a/symfony/mailer/Transport/Smtp/SmtpTransport.php b/symfony/mailer/Transport/Smtp/SmtpTransport.php index 92af6aaf6..b01bb37de 100644 --- a/symfony/mailer/Transport/Smtp/SmtpTransport.php +++ b/symfony/mailer/Transport/Smtp/SmtpTransport.php @@ -40,7 +40,7 @@ class SmtpTransport extends AbstractTransport private $stream; private $domain = '[127.0.0.1]'; - public function __construct(AbstractStream $stream = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + public function __construct(?AbstractStream $stream = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) { parent::__construct($dispatcher, $logger); @@ -130,7 +130,7 @@ public function getLocalDomain(): string return $this->domain; } - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { try { $message = parent::send($message, $envelope); diff --git a/symfony/mailer/Transport/Smtp/Stream/AbstractStream.php b/symfony/mailer/Transport/Smtp/Stream/AbstractStream.php index 4cfcd8b54..2be8fce94 100644 --- a/symfony/mailer/Transport/Smtp/Stream/AbstractStream.php +++ b/symfony/mailer/Transport/Smtp/Stream/AbstractStream.php @@ -27,6 +27,7 @@ abstract class AbstractStream protected $stream; protected $in; protected $out; + protected $err; private $debug = ''; @@ -65,7 +66,7 @@ abstract public function initialize(): void; public function terminate(): void { - $this->stream = $this->out = $this->in = null; + $this->stream = $this->err = $this->out = $this->in = null; } public function readLine(): string @@ -74,7 +75,7 @@ public function readLine(): string return ''; } - $line = fgets($this->out); + $line = @fgets($this->out); if ('' === $line || false === $line) { $metas = stream_get_meta_data($this->out); if ($metas['timed_out']) { @@ -83,6 +84,9 @@ public function readLine(): string if ($metas['eof']) { throw new TransportException(sprintf('Connection to "%s" has been closed unexpectedly.', $this->getReadConnectionDescription())); } + if (false === $line) { + throw new TransportException(sprintf('Unable to read from connection to "%s": ', $this->getReadConnectionDescription()).error_get_last()['message']); + } } $this->debug .= sprintf('< %s', $line); diff --git a/symfony/mailer/Transport/Smtp/Stream/ProcessStream.php b/symfony/mailer/Transport/Smtp/Stream/ProcessStream.php index bc721ad0c..d717055b6 100644 --- a/symfony/mailer/Transport/Smtp/Stream/ProcessStream.php +++ b/symfony/mailer/Transport/Smtp/Stream/ProcessStream.php @@ -25,11 +25,18 @@ final class ProcessStream extends AbstractStream { private $command; + private $interactive = false; + public function setCommand(string $command) { $this->command = $command; } + public function setInteractive(bool $interactive) + { + $this->interactive = $interactive; + } + public function initialize(): void { $descriptorSpec = [ @@ -45,17 +52,27 @@ public function initialize(): void } $this->in = &$pipes[0]; $this->out = &$pipes[1]; + $this->err = &$pipes[2]; } public function terminate(): void { if (null !== $this->stream) { fclose($this->in); + $out = stream_get_contents($this->out); fclose($this->out); - proc_close($this->stream); + $err = stream_get_contents($this->err); + fclose($this->err); + if (0 !== $exitCode = proc_close($this->stream)) { + $errorMessage = 'Process failed with exit code '.$exitCode.': '.$out.$err; + } } parent::terminate(); + + if (!$this->interactive && isset($errorMessage)) { + throw new TransportException($errorMessage); + } } protected function getReadConnectionDescription(): string diff --git a/symfony/mailer/Transport/Smtp/Stream/SocketStream.php b/symfony/mailer/Transport/Smtp/Stream/SocketStream.php index 368fbd28c..e2db24875 100644 --- a/symfony/mailer/Transport/Smtp/Stream/SocketStream.php +++ b/symfony/mailer/Transport/Smtp/Stream/SocketStream.php @@ -160,7 +160,7 @@ public function initialize(): void } stream_set_blocking($this->stream, true); - stream_set_timeout($this->stream, $timeout); + stream_set_timeout($this->stream, (int) $timeout, (int) (($timeout - (int) $timeout) * 1000000)); $this->in = &$this->stream; $this->out = &$this->stream; } diff --git a/symfony/mailer/Transport/TransportInterface.php b/symfony/mailer/Transport/TransportInterface.php index ed562cfef..25c2e591e 100644 --- a/symfony/mailer/Transport/TransportInterface.php +++ b/symfony/mailer/Transport/TransportInterface.php @@ -29,7 +29,7 @@ interface TransportInterface /** * @throws TransportExceptionInterface */ - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage; + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage; public function __toString(): string; } diff --git a/symfony/mailer/Transport/Transports.php b/symfony/mailer/Transport/Transports.php index 702fc5c78..63daa3830 100644 --- a/symfony/mailer/Transport/Transports.php +++ b/symfony/mailer/Transport/Transports.php @@ -44,7 +44,7 @@ public function __construct(iterable $transports) } } - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { /** @var Message $message */ if (RawMessage::class === \get_class($message) || !$message->getHeaders()->has('X-Transport')) { diff --git a/symfony/process/ExecutableFinder.php b/symfony/process/ExecutableFinder.php index eb8f06292..89edd22fb 100644 --- a/symfony/process/ExecutableFinder.php +++ b/symfony/process/ExecutableFinder.php @@ -19,7 +19,15 @@ */ class ExecutableFinder { - private $suffixes = ['.exe', '.bat', '.cmd', '.com']; + private const CMD_BUILTINS = [ + 'assoc', 'break', 'call', 'cd', 'chdir', 'cls', 'color', 'copy', 'date', + 'del', 'dir', 'echo', 'endlocal', 'erase', 'exit', 'for', 'ftype', 'goto', + 'help', 'if', 'label', 'md', 'mkdir', 'mklink', 'move', 'path', 'pause', + 'popd', 'prompt', 'pushd', 'rd', 'rem', 'ren', 'rename', 'rmdir', 'set', + 'setlocal', 'shift', 'start', 'time', 'title', 'type', 'ver', 'vol', + ]; + + private $suffixes = []; /** * Replaces default suffixes of executable. @@ -46,41 +54,50 @@ public function addSuffix(string $suffix) * * @return string|null */ - public function find(string $name, string $default = null, array $extraDirs = []) + public function find(string $name, ?string $default = null, array $extraDirs = []) { - if (\ini_get('open_basedir')) { - $searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs); - $dirs = []; - foreach ($searchPath as $path) { - // Silencing against https://bugs.php.net/69240 - if (@is_dir($path)) { - $dirs[] = $path; - } else { - if (basename($path) == $name && @is_executable($path)) { - return $path; - } - } - } - } else { - $dirs = array_merge( - explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')), - $extraDirs - ); + // windows built-in commands that are present in cmd.exe should not be resolved using PATH as they do not exist as exes + if ('\\' === \DIRECTORY_SEPARATOR && \in_array(strtolower($name), self::CMD_BUILTINS, true)) { + return $name; } - $suffixes = ['']; + $dirs = array_merge( + explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')), + $extraDirs + ); + + $suffixes = []; if ('\\' === \DIRECTORY_SEPARATOR) { $pathExt = getenv('PATHEXT'); - $suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes); + $suffixes = $this->suffixes; + $suffixes = array_merge($suffixes, $pathExt ? explode(\PATH_SEPARATOR, $pathExt) : ['.exe', '.bat', '.cmd', '.com']); } + $suffixes = '' !== pathinfo($name, PATHINFO_EXTENSION) ? array_merge([''], $suffixes) : array_merge($suffixes, ['']); foreach ($suffixes as $suffix) { foreach ($dirs as $dir) { + if ('' === $dir) { + $dir = '.'; + } if (@is_file($file = $dir.\DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === \DIRECTORY_SEPARATOR || @is_executable($file))) { return $file; } + + if (!@is_dir($dir) && basename($dir) === $name.$suffix && @is_executable($dir)) { + return $dir; + } } } + if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('exec') || \strlen($name) !== strcspn($name, '/'.\DIRECTORY_SEPARATOR)) { + return $default; + } + + $execResult = exec('command -v -- '.escapeshellarg($name)); + + if (($executablePath = substr($execResult, 0, strpos($execResult, \PHP_EOL) ?: null)) && @is_executable($executablePath)) { + return $executablePath; + } + return $default; } } diff --git a/symfony/process/InputStream.php b/symfony/process/InputStream.php index 240665f32..0c45b5245 100644 --- a/symfony/process/InputStream.php +++ b/symfony/process/InputStream.php @@ -30,7 +30,7 @@ class InputStream implements \IteratorAggregate /** * Sets a callback that is called when the write buffer becomes empty. */ - public function onEmpty(callable $onEmpty = null) + public function onEmpty(?callable $onEmpty = null) { $this->onEmpty = $onEmpty; } diff --git a/symfony/process/PhpExecutableFinder.php b/symfony/process/PhpExecutableFinder.php index bed6c3dc8..c3a9680d7 100644 --- a/symfony/process/PhpExecutableFinder.php +++ b/symfony/process/PhpExecutableFinder.php @@ -34,15 +34,8 @@ public function __construct() public function find(bool $includeArgs = true) { if ($php = getenv('PHP_BINARY')) { - if (!is_executable($php)) { - $command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v'; - if ($php = strtok(exec($command.' '.escapeshellarg($php)), \PHP_EOL)) { - if (!is_executable($php)) { - return false; - } - } else { - return false; - } + if (!is_executable($php) && !$php = $this->executableFinder->find($php)) { + return false; } if (@is_dir($php)) { diff --git a/symfony/process/PhpProcess.php b/symfony/process/PhpProcess.php index 2bc338e5e..3a1d147c8 100644 --- a/symfony/process/PhpProcess.php +++ b/symfony/process/PhpProcess.php @@ -32,7 +32,7 @@ class PhpProcess extends Process * @param int $timeout The timeout in seconds * @param array|null $php Path to the PHP binary to use with any additional arguments */ - public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null) + public function __construct(string $script, ?string $cwd = null, ?array $env = null, int $timeout = 60, ?array $php = null) { if (null === $php) { $executableFinder = new PhpExecutableFinder(); @@ -53,7 +53,7 @@ public function __construct(string $script, string $cwd = null, array $env = nul /** * {@inheritdoc} */ - public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + public static function fromShellCommandline(string $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60) { throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class)); } @@ -61,7 +61,7 @@ public static function fromShellCommandline(string $command, string $cwd = null, /** * {@inheritdoc} */ - public function start(callable $callback = null, array $env = []) + public function start(?callable $callback = null, array $env = []) { if (null === $this->getCommandLine()) { throw new RuntimeException('Unable to find the PHP executable.'); diff --git a/symfony/process/Process.php b/symfony/process/Process.php index 30ebeb6b5..91f9e8fee 100644 --- a/symfony/process/Process.php +++ b/symfony/process/Process.php @@ -80,6 +80,7 @@ class Process implements \IteratorAggregate private $processPipes; private $latestSignal; + private $cachedExitCode; private static $sigchild; @@ -140,7 +141,7 @@ class Process implements \IteratorAggregate * * @throws LogicException When proc_open is not installed */ - public function __construct(array $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + public function __construct(array $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60) { if (!\function_exists('proc_open')) { throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.'); @@ -189,7 +190,7 @@ public function __construct(array $command, string $cwd = null, array $env = nul * * @throws LogicException When proc_open is not installed */ - public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + public static function fromShellCommandline(string $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60) { $process = new static([], $cwd, $env, $input, $timeout); $process->commandline = $command; @@ -247,7 +248,7 @@ public function __clone() * * @final */ - public function run(callable $callback = null, array $env = []): int + public function run(?callable $callback = null, array $env = []): int { $this->start($callback, $env); @@ -266,7 +267,7 @@ public function run(callable $callback = null, array $env = []): int * * @final */ - public function mustRun(callable $callback = null, array $env = []): self + public function mustRun(?callable $callback = null, array $env = []): self { if (0 !== $this->run($callback, $env)) { throw new ProcessFailedException($this); @@ -294,7 +295,7 @@ public function mustRun(callable $callback = null, array $env = []): self * @throws RuntimeException When process is already running * @throws LogicException In case a callback is provided and output has been disabled */ - public function start(callable $callback = null, array $env = []) + public function start(?callable $callback = null, array $env = []) { if ($this->isRunning()) { throw new RuntimeException('Process is already running.'); @@ -351,7 +352,7 @@ public function start(callable $callback = null, array $env = []) $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options); - if (!\is_resource($this->process)) { + if (!$this->process) { throw new RuntimeException('Unable to launch a new process.'); } $this->status = self::STATUS_STARTED; @@ -385,7 +386,7 @@ public function start(callable $callback = null, array $env = []) * * @final */ - public function restart(callable $callback = null, array $env = []): self + public function restart(?callable $callback = null, array $env = []): self { if ($this->isRunning()) { throw new RuntimeException('Process is already running.'); @@ -412,7 +413,7 @@ public function restart(callable $callback = null, array $env = []): self * @throws ProcessSignaledException When process stopped after receiving signal * @throws LogicException When process is not yet started */ - public function wait(callable $callback = null) + public function wait(?callable $callback = null) { $this->requireProcessIsStarted(__FUNCTION__); @@ -914,7 +915,7 @@ public function getStatus() * * @return int|null The exit-code of the process or null if it's not running */ - public function stop(float $timeout = 10, int $signal = null) + public function stop(float $timeout = 10, ?int $signal = null) { $timeoutMicro = microtime(true) + $timeout; if ($this->isRunning()) { @@ -1310,7 +1311,7 @@ private function getDescriptors(): array * * @return \Closure */ - protected function buildCallback(callable $callback = null) + protected function buildCallback(?callable $callback = null) { if ($this->outputDisabled) { return function ($type, $data) use ($callback): bool { @@ -1345,6 +1346,19 @@ protected function updateStatus(bool $blocking) $this->processInformation = proc_get_status($this->process); $running = $this->processInformation['running']; + // In PHP < 8.3, "proc_get_status" only returns the correct exit status on the first call. + // Subsequent calls return -1 as the process is discarded. This workaround caches the first + // retrieved exit status for consistent results in later calls, mimicking PHP 8.3 behavior. + if (\PHP_VERSION_ID < 80300) { + if (!isset($this->cachedExitCode) && !$running && -1 !== $this->processInformation['exitcode']) { + $this->cachedExitCode = $this->processInformation['exitcode']; + } + + if (isset($this->cachedExitCode) && !$running && -1 === $this->processInformation['exitcode']) { + $this->processInformation['exitcode'] = $this->cachedExitCode; + } + } + $this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running); if ($this->fallbackStatus && $this->isSigchildEnabled()) { @@ -1442,8 +1456,9 @@ private function readPipes(bool $blocking, bool $close) private function close(): int { $this->processPipes->close(); - if (\is_resource($this->process)) { + if ($this->process) { proc_close($this->process); + $this->process = null; } $this->exitcode = $this->processInformation['exitcode']; $this->status = self::STATUS_TERMINATED; @@ -1577,7 +1592,14 @@ function ($m) use (&$env, &$varCache, &$varCount, $uid) { $cmd ); - $cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')'; + static $comSpec; + + if (!$comSpec && $comSpec = (new ExecutableFinder())->find('cmd.exe')) { + // Escape according to CommandLineToArgvW rules + $comSpec = '"'.preg_replace('{(\\\\*+)"}', '$1$1\"', $comSpec) .'"'; + } + + $cmd = ($comSpec ?? 'cmd').' /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')'; foreach ($this->processPipes->getFiles() as $offset => $filename) { $cmd .= ' '.$offset.'>"'.$filename.'"'; } @@ -1623,7 +1645,7 @@ private function escapeArgument(?string $argument): string if (str_contains($argument, "\0")) { $argument = str_replace("\0", '?', $argument); } - if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) { + if (!preg_match('/[()%!^"<>&|\s]/', $argument)) { return $argument; } $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument); diff --git a/symfony/routing/Annotation/Route.php b/symfony/routing/Annotation/Route.php index 81563df20..957344f01 100644 --- a/symfony/routing/Annotation/Route.php +++ b/symfony/routing/Annotation/Route.php @@ -49,20 +49,20 @@ class Route public function __construct( $data = [], $path = null, - string $name = null, + ?string $name = null, array $requirements = [], array $options = [], array $defaults = [], - string $host = null, + ?string $host = null, $methods = [], $schemes = [], - string $condition = null, - int $priority = null, - string $locale = null, - string $format = null, - bool $utf8 = null, - bool $stateless = null, - string $env = null + ?string $condition = null, + ?int $priority = null, + ?string $locale = null, + ?string $format = null, + ?bool $utf8 = null, + ?bool $stateless = null, + ?string $env = null ) { if (\is_string($data)) { $data = ['path' => $data]; diff --git a/symfony/routing/CompiledRoute.php b/symfony/routing/CompiledRoute.php index 1449cdb92..64bf9cacd 100644 --- a/symfony/routing/CompiledRoute.php +++ b/symfony/routing/CompiledRoute.php @@ -37,7 +37,7 @@ class CompiledRoute implements \Serializable * @param array $hostVariables An array of host variables * @param array $variables An array of variables (variables defined in the path and in the host patterns) */ - public function __construct(string $staticPrefix, string $regex, array $tokens, array $pathVariables, string $hostRegex = null, array $hostTokens = [], array $hostVariables = [], array $variables = []) + public function __construct(string $staticPrefix, string $regex, array $tokens, array $pathVariables, ?string $hostRegex = null, array $hostTokens = [], array $hostVariables = [], array $variables = []) { $this->staticPrefix = $staticPrefix; $this->regex = $regex; diff --git a/symfony/routing/Exception/MethodNotAllowedException.php b/symfony/routing/Exception/MethodNotAllowedException.php index 27cf2125e..a73e1e6e3 100644 --- a/symfony/routing/Exception/MethodNotAllowedException.php +++ b/symfony/routing/Exception/MethodNotAllowedException.php @@ -25,7 +25,7 @@ class MethodNotAllowedException extends \RuntimeException implements ExceptionIn /** * @param string[] $allowedMethods */ - public function __construct(array $allowedMethods, ?string $message = '', int $code = 0, \Throwable $previous = null) + public function __construct(array $allowedMethods, ?string $message = '', int $code = 0, ?\Throwable $previous = null) { if (null === $message) { trigger_deprecation('symfony/routing', '5.3', 'Passing null as $message to "%s()" is deprecated, pass an empty string instead.', __METHOD__); diff --git a/symfony/routing/Generator/CompiledUrlGenerator.php b/symfony/routing/Generator/CompiledUrlGenerator.php index 8cbbf8f70..8af3ae78e 100644 --- a/symfony/routing/Generator/CompiledUrlGenerator.php +++ b/symfony/routing/Generator/CompiledUrlGenerator.php @@ -23,7 +23,7 @@ class CompiledUrlGenerator extends UrlGenerator private $compiledRoutes = []; private $defaultLocale; - public function __construct(array $compiledRoutes, RequestContext $context, LoggerInterface $logger = null, string $defaultLocale = null) + public function __construct(array $compiledRoutes, RequestContext $context, ?LoggerInterface $logger = null, ?string $defaultLocale = null) { $this->compiledRoutes = $compiledRoutes; $this->context = $context; diff --git a/symfony/routing/Generator/UrlGenerator.php b/symfony/routing/Generator/UrlGenerator.php index d27b00004..4419e9efd 100644 --- a/symfony/routing/Generator/UrlGenerator.php +++ b/symfony/routing/Generator/UrlGenerator.php @@ -82,7 +82,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt '%7C' => '|', ]; - public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null, string $defaultLocale = null) + public function __construct(RouteCollection $routes, RequestContext $context, ?LoggerInterface $logger = null, ?string $defaultLocale = null) { $this->routes = $routes; $this->context = $context; diff --git a/symfony/routing/Loader/AnnotationClassLoader.php b/symfony/routing/Loader/AnnotationClassLoader.php index ad5af5c94..c0bcb4713 100644 --- a/symfony/routing/Loader/AnnotationClassLoader.php +++ b/symfony/routing/Loader/AnnotationClassLoader.php @@ -85,7 +85,7 @@ abstract class AnnotationClassLoader implements LoaderInterface */ protected $defaultRouteIndex = 0; - public function __construct(Reader $reader = null, string $env = null) + public function __construct(?Reader $reader = null, ?string $env = null) { $this->reader = $reader; $this->env = $env; @@ -108,7 +108,7 @@ public function setRouteAnnotationClass(string $class) * * @throws \InvalidArgumentException When route can't be parsed */ - public function load($class, string $type = null) + public function load($class, ?string $type = null) { if (!class_exists($class)) { throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); @@ -239,7 +239,7 @@ protected function addRoute(RouteCollection $collection, object $annot, array $g /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return \is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type); } diff --git a/symfony/routing/Loader/AnnotationDirectoryLoader.php b/symfony/routing/Loader/AnnotationDirectoryLoader.php index ae825a39f..8cd60f827 100644 --- a/symfony/routing/Loader/AnnotationDirectoryLoader.php +++ b/symfony/routing/Loader/AnnotationDirectoryLoader.php @@ -32,7 +32,7 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader * * @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed */ - public function load($path, string $type = null) + public function load($path, ?string $type = null) { if (!is_dir($dir = $this->locator->locate($path))) { return parent::supports($path, $type) ? parent::load($path, $type) : new RouteCollection(); @@ -74,7 +74,7 @@ function (\SplFileInfo $current) { /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { if ('annotation' === $type) { return true; diff --git a/symfony/routing/Loader/AnnotationFileLoader.php b/symfony/routing/Loader/AnnotationFileLoader.php index 27af66ee6..e75eac11c 100644 --- a/symfony/routing/Loader/AnnotationFileLoader.php +++ b/symfony/routing/Loader/AnnotationFileLoader.php @@ -47,7 +47,7 @@ public function __construct(FileLocatorInterface $locator, AnnotationClassLoader * * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed */ - public function load($file, string $type = null) + public function load($file, ?string $type = null) { $path = $this->locator->locate($file); @@ -70,7 +70,7 @@ public function load($file, string $type = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'annotation' === $type); } @@ -87,7 +87,7 @@ protected function findClass(string $file) $tokens = token_get_all(file_get_contents($file)); if (1 === \count($tokens) && \T_INLINE_HTML === $tokens[0][0]) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the " true, \T_STRING => true]; diff --git a/symfony/routing/Loader/ClosureLoader.php b/symfony/routing/Loader/ClosureLoader.php index 42f950f50..a5081ca28 100644 --- a/symfony/routing/Loader/ClosureLoader.php +++ b/symfony/routing/Loader/ClosureLoader.php @@ -31,7 +31,7 @@ class ClosureLoader extends Loader * * @return RouteCollection */ - public function load($closure, string $type = null) + public function load($closure, ?string $type = null) { return $closure($this->env); } @@ -39,7 +39,7 @@ public function load($closure, string $type = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return $resource instanceof \Closure && (!$type || 'closure' === $type); } diff --git a/symfony/routing/Loader/Configurator/CollectionConfigurator.php b/symfony/routing/Loader/Configurator/CollectionConfigurator.php index 09274ccdc..ec59f7ee9 100644 --- a/symfony/routing/Loader/Configurator/CollectionConfigurator.php +++ b/symfony/routing/Loader/Configurator/CollectionConfigurator.php @@ -28,7 +28,7 @@ class CollectionConfigurator private $parentPrefixes; private $host; - public function __construct(RouteCollection $parent, string $name, self $parentConfigurator = null, array $parentPrefixes = null) + public function __construct(RouteCollection $parent, string $name, ?self $parentConfigurator = null, ?array $parentPrefixes = null) { $this->parent = $parent; $this->name = $name; diff --git a/symfony/routing/Loader/Configurator/RouteConfigurator.php b/symfony/routing/Loader/Configurator/RouteConfigurator.php index bb6ce267a..fcd1c2157 100644 --- a/symfony/routing/Loader/Configurator/RouteConfigurator.php +++ b/symfony/routing/Loader/Configurator/RouteConfigurator.php @@ -24,7 +24,7 @@ class RouteConfigurator protected $parentConfigurator; - public function __construct(RouteCollection $collection, RouteCollection $route, string $name = '', CollectionConfigurator $parentConfigurator = null, array $prefixes = null) + public function __construct(RouteCollection $collection, RouteCollection $route, string $name = '', ?CollectionConfigurator $parentConfigurator = null, ?array $prefixes = null) { $this->collection = $collection; $this->route = $route; diff --git a/symfony/routing/Loader/Configurator/RoutingConfigurator.php b/symfony/routing/Loader/Configurator/RoutingConfigurator.php index 4687bf681..620b2d586 100644 --- a/symfony/routing/Loader/Configurator/RoutingConfigurator.php +++ b/symfony/routing/Loader/Configurator/RoutingConfigurator.php @@ -26,7 +26,7 @@ class RoutingConfigurator private $file; private $env; - public function __construct(RouteCollection $collection, PhpFileLoader $loader, string $path, string $file, string $env = null) + public function __construct(RouteCollection $collection, PhpFileLoader $loader, string $path, string $file, ?string $env = null) { $this->collection = $collection; $this->loader = $loader; @@ -38,7 +38,7 @@ public function __construct(RouteCollection $collection, PhpFileLoader $loader, /** * @param string|string[]|null $exclude Glob patterns to exclude from the import */ - final public function import($resource, string $type = null, bool $ignoreErrors = false, $exclude = null): ImportConfigurator + final public function import($resource, ?string $type = null, bool $ignoreErrors = false, $exclude = null): ImportConfigurator { $this->loader->setCurrentDir(\dirname($this->path)); diff --git a/symfony/routing/Loader/Configurator/Traits/HostTrait.php b/symfony/routing/Loader/Configurator/Traits/HostTrait.php index 54ae6566a..168bbb4f9 100644 --- a/symfony/routing/Loader/Configurator/Traits/HostTrait.php +++ b/symfony/routing/Loader/Configurator/Traits/HostTrait.php @@ -28,6 +28,7 @@ final protected function addHost(RouteCollection $routes, $hosts) foreach ($routes->all() as $name => $route) { if (null === $locale = $route->getDefault('_locale')) { + $priority = $routes->getPriority($name) ?? 0; $routes->remove($name); foreach ($hosts as $locale => $host) { $localizedRoute = clone $route; @@ -35,14 +36,14 @@ final protected function addHost(RouteCollection $routes, $hosts) $localizedRoute->setRequirement('_locale', preg_quote($locale)); $localizedRoute->setDefault('_canonical_route', $name); $localizedRoute->setHost($host); - $routes->add($name.'.'.$locale, $localizedRoute); + $routes->add($name.'.'.$locale, $localizedRoute, $priority); } } elseif (!isset($hosts[$locale])) { throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding host in its parent collection.', $name, $locale)); } else { $route->setHost($hosts[$locale]); $route->setRequirement('_locale', preg_quote($locale)); - $routes->add($name, $route); + $routes->add($name, $route, $routes->getPriority($name) ?? 0); } } } diff --git a/symfony/routing/Loader/Configurator/Traits/LocalizedRouteTrait.php b/symfony/routing/Loader/Configurator/Traits/LocalizedRouteTrait.php index 4734a4eac..44fb047a9 100644 --- a/symfony/routing/Loader/Configurator/Traits/LocalizedRouteTrait.php +++ b/symfony/routing/Loader/Configurator/Traits/LocalizedRouteTrait.php @@ -27,7 +27,7 @@ trait LocalizedRouteTrait * * @param string|array $path the path, or the localized paths of the route */ - final protected function createLocalizedRoute(RouteCollection $collection, string $name, $path, string $namePrefix = '', array $prefixes = null): RouteCollection + final protected function createLocalizedRoute(RouteCollection $collection, string $name, $path, string $namePrefix = '', ?array $prefixes = null): RouteCollection { $paths = []; diff --git a/symfony/routing/Loader/Configurator/Traits/PrefixTrait.php b/symfony/routing/Loader/Configurator/Traits/PrefixTrait.php index 27053bcaf..0b19573ec 100644 --- a/symfony/routing/Loader/Configurator/Traits/PrefixTrait.php +++ b/symfony/routing/Loader/Configurator/Traits/PrefixTrait.php @@ -29,6 +29,7 @@ final protected function addPrefix(RouteCollection $routes, $prefix, bool $trail } foreach ($routes->all() as $name => $route) { if (null === $locale = $route->getDefault('_locale')) { + $priority = $routes->getPriority($name) ?? 0; $routes->remove($name); foreach ($prefix as $locale => $localePrefix) { $localizedRoute = clone $route; @@ -36,13 +37,13 @@ final protected function addPrefix(RouteCollection $routes, $prefix, bool $trail $localizedRoute->setRequirement('_locale', preg_quote($locale)); $localizedRoute->setDefault('_canonical_route', $name); $localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); - $routes->add($name.'.'.$locale, $localizedRoute); + $routes->add($name.'.'.$locale, $localizedRoute, $priority); } } elseif (!isset($prefix[$locale])) { throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale)); } else { $route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); - $routes->add($name, $route); + $routes->add($name, $route, $routes->getPriority($name) ?? 0); } } diff --git a/symfony/routing/Loader/ContainerLoader.php b/symfony/routing/Loader/ContainerLoader.php index d8730aec6..a03d46524 100644 --- a/symfony/routing/Loader/ContainerLoader.php +++ b/symfony/routing/Loader/ContainerLoader.php @@ -22,7 +22,7 @@ class ContainerLoader extends ObjectLoader { private $container; - public function __construct(ContainerInterface $container, string $env = null) + public function __construct(ContainerInterface $container, ?string $env = null) { $this->container = $container; parent::__construct($env); @@ -31,7 +31,7 @@ public function __construct(ContainerInterface $container, string $env = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return 'service' === $type && \is_string($resource); } diff --git a/symfony/routing/Loader/DirectoryLoader.php b/symfony/routing/Loader/DirectoryLoader.php index c0f349177..24cf185d6 100644 --- a/symfony/routing/Loader/DirectoryLoader.php +++ b/symfony/routing/Loader/DirectoryLoader.php @@ -20,7 +20,7 @@ class DirectoryLoader extends FileLoader /** * {@inheritdoc} */ - public function load($file, string $type = null) + public function load($file, ?string $type = null) { $path = $this->locator->locate($file); @@ -49,7 +49,7 @@ public function load($file, string $type = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { // only when type is forced to directory, not to conflict with AnnotationLoader diff --git a/symfony/routing/Loader/GlobFileLoader.php b/symfony/routing/Loader/GlobFileLoader.php index 780fb15dc..9c2f4ed4f 100644 --- a/symfony/routing/Loader/GlobFileLoader.php +++ b/symfony/routing/Loader/GlobFileLoader.php @@ -24,7 +24,7 @@ class GlobFileLoader extends FileLoader /** * {@inheritdoc} */ - public function load($resource, string $type = null) + public function load($resource, ?string $type = null) { $collection = new RouteCollection(); @@ -40,7 +40,7 @@ public function load($resource, string $type = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return 'glob' === $type; } diff --git a/symfony/routing/Loader/ObjectLoader.php b/symfony/routing/Loader/ObjectLoader.php index 062453908..d212f8e8b 100644 --- a/symfony/routing/Loader/ObjectLoader.php +++ b/symfony/routing/Loader/ObjectLoader.php @@ -40,7 +40,7 @@ abstract protected function getObject(string $id); * * @return RouteCollection */ - public function load($resource, string $type = null) + public function load($resource, ?string $type = null) { if (!preg_match('/^[^\:]+(?:::(?:[^\:]+))?$/', $resource)) { throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the %s route loader: use the format "object_id::method" or "object_id" if your object class has an "__invoke" method.', $resource, \is_string($type) ? '"'.$type.'"' : 'object')); diff --git a/symfony/routing/Loader/PhpFileLoader.php b/symfony/routing/Loader/PhpFileLoader.php index 39ac81273..3f1cf9cd1 100644 --- a/symfony/routing/Loader/PhpFileLoader.php +++ b/symfony/routing/Loader/PhpFileLoader.php @@ -35,7 +35,7 @@ class PhpFileLoader extends FileLoader * * @return RouteCollection */ - public function load($file, string $type = null) + public function load($file, ?string $type = null) { $path = $this->locator->locate($file); $this->setCurrentDir(\dirname($path)); @@ -62,7 +62,7 @@ public function load($file, string $type = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'php' === $type); } diff --git a/symfony/routing/Loader/XmlFileLoader.php b/symfony/routing/Loader/XmlFileLoader.php index 220153364..85bb0ee8c 100644 --- a/symfony/routing/Loader/XmlFileLoader.php +++ b/symfony/routing/Loader/XmlFileLoader.php @@ -45,7 +45,7 @@ class XmlFileLoader extends FileLoader * @throws \InvalidArgumentException when the file cannot be loaded or when the XML cannot be * parsed because it does not validate against the scheme */ - public function load($file, string $type = null) + public function load($file, ?string $type = null) { $path = $this->locator->locate($file); @@ -102,7 +102,7 @@ protected function parseNode(RouteCollection $collection, \DOMElement $node, str /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return \is_string($resource) && 'xml' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'xml' === $type); } diff --git a/symfony/routing/Loader/YamlFileLoader.php b/symfony/routing/Loader/YamlFileLoader.php index ae98a314e..1087817bb 100644 --- a/symfony/routing/Loader/YamlFileLoader.php +++ b/symfony/routing/Loader/YamlFileLoader.php @@ -48,7 +48,7 @@ class YamlFileLoader extends FileLoader * * @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid */ - public function load($file, string $type = null) + public function load($file, ?string $type = null) { $path = $this->locator->locate($file); @@ -117,7 +117,7 @@ public function load($file, string $type = null) /** * {@inheritdoc} */ - public function supports($resource, string $type = null) + public function supports($resource, ?string $type = null) { return \is_string($resource) && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yml', 'yaml'], true) && (!$type || 'yaml' === $type); } diff --git a/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php b/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php index 4b3888045..ddf231f05 100644 --- a/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php +++ b/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php @@ -139,8 +139,7 @@ private function generateCompiledRoutes(): string foreach ($staticRoutes as $path => $routes) { $code .= sprintf(" %s => [\n", self::export($path)); foreach ($routes as $route) { - $r = array_map([__CLASS__, 'export'], $route); - $code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", $r[0], $r[1], $r[2], $r[3], $r[4], $r[5], $r[6]); + $code .= vsprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", array_map([__CLASS__, 'export'], $route)); } $code .= " ],\n"; } @@ -152,8 +151,7 @@ private function generateCompiledRoutes(): string foreach ($dynamicRoutes as $path => $routes) { $code .= sprintf(" %s => [\n", self::export($path)); foreach ($routes as $route) { - $r = array_map([__CLASS__, 'export'], $route); - $code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", $r[0], $r[1], $r[2], $r[3], $r[4], $r[5], $r[6]); + $code .= vsprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", array_map([__CLASS__, 'export'], $route)); } $code .= " ],\n"; } diff --git a/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php b/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php index 97bd692a5..47d923c66 100644 --- a/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php +++ b/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php @@ -200,6 +200,7 @@ private function getCommonPrefix(string $prefix, string $anotherPrefix): array public static function handleError(int $type, string $msg) { - return str_contains($msg, 'Compilation failed: lookbehind assertion is not fixed length'); + return str_contains($msg, 'Compilation failed: lookbehind assertion is not fixed length') + || str_contains($msg, 'Compilation failed: length of lookbehind assertion is not limited'); } } diff --git a/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php b/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php index d07f42093..a43888b3e 100644 --- a/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php +++ b/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php @@ -27,5 +27,5 @@ interface RedirectableUrlMatcherInterface * * @return array */ - public function redirect(string $path, string $route, string $scheme = null); + public function redirect(string $path, string $route, ?string $scheme = null); } diff --git a/symfony/routing/Matcher/TraceableUrlMatcher.php b/symfony/routing/Matcher/TraceableUrlMatcher.php index 9e8c4c42d..cddfe0254 100644 --- a/symfony/routing/Matcher/TraceableUrlMatcher.php +++ b/symfony/routing/Matcher/TraceableUrlMatcher.php @@ -152,7 +152,7 @@ protected function matchCollection(string $pathinfo, RouteCollection $routes) return []; } - private function addTrace(string $log, int $level = self::ROUTE_DOES_NOT_MATCH, string $name = null, Route $route = null) + private function addTrace(string $log, int $level = self::ROUTE_DOES_NOT_MATCH, ?string $name = null, ?Route $route = null) { $this->traces[] = [ 'log' => $log, diff --git a/symfony/routing/RouteCollection.php b/symfony/routing/RouteCollection.php index a0700bba3..95faead6e 100644 --- a/symfony/routing/RouteCollection.php +++ b/symfony/routing/RouteCollection.php @@ -157,9 +157,24 @@ public function get(string $name) */ public function remove($name) { + $routes = []; foreach ((array) $name as $n) { + if (isset($this->routes[$n])) { + $routes[] = $n; + } + unset($this->routes[$n], $this->priorities[$n], $this->aliases[$n]); } + + if (!$routes) { + return; + } + + foreach ($this->aliases as $k => $alias) { + if (\in_array($alias->getId(), $routes, true)) { + unset($this->aliases[$k]); + } + } } /** @@ -380,4 +395,9 @@ public function getAlias(string $name): ?Alias { return $this->aliases[$name] ?? null; } + + public function getPriority(string $name): ?int + { + return $this->priorities[$name] ?? null; + } } diff --git a/symfony/routing/RouteCollectionBuilder.php b/symfony/routing/RouteCollectionBuilder.php index d7eed31eb..04a443972 100644 --- a/symfony/routing/RouteCollectionBuilder.php +++ b/symfony/routing/RouteCollectionBuilder.php @@ -43,7 +43,7 @@ class RouteCollectionBuilder private $methods; private $resources = []; - public function __construct(LoaderInterface $loader = null) + public function __construct(?LoaderInterface $loader = null) { $this->loader = $loader; } @@ -59,7 +59,7 @@ public function __construct(LoaderInterface $loader = null) * * @throws LoaderLoadException */ - public function import($resource, string $prefix = '/', string $type = null) + public function import($resource, string $prefix = '/', ?string $type = null) { /** @var RouteCollection[] $collections */ $collections = $this->load($resource, $type); @@ -92,7 +92,7 @@ public function import($resource, string $prefix = '/', string $type = null) * * @return Route */ - public function add(string $path, string $controller, string $name = null) + public function add(string $path, string $controller, ?string $name = null) { $route = new Route($path); $route->setDefault('_controller', $controller); @@ -125,7 +125,7 @@ public function mount(string $prefix, self $builder) * * @return $this */ - public function addRoute(Route $route, string $name = null) + public function addRoute(Route $route, ?string $name = null) { if (null === $name) { // used as a flag to know which routes will need a name later @@ -337,7 +337,7 @@ private function generateRouteName(Route $route): string * * @throws LoaderLoadException If no loader is found */ - private function load($resource, string $type = null): array + private function load($resource, ?string $type = null): array { if (null === $this->loader) { throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.'); diff --git a/symfony/routing/Router.php b/symfony/routing/Router.php index 25b9456af..e3d38c491 100644 --- a/symfony/routing/Router.php +++ b/symfony/routing/Router.php @@ -97,7 +97,7 @@ class Router implements RouterInterface, RequestMatcherInterface /** * @param mixed $resource The main resource to load */ - public function __construct(LoaderInterface $loader, $resource, array $options = [], RequestContext $context = null, LoggerInterface $logger = null, string $defaultLocale = null) + public function __construct(LoaderInterface $loader, $resource, array $options = [], ?RequestContext $context = null, ?LoggerInterface $logger = null, ?string $defaultLocale = null) { $this->loader = $loader; $this->resource = $resource; @@ -294,6 +294,7 @@ function (ConfigCacheInterface $cache) { } $cache->write($dumper->dump(), $this->getRouteCollection()->getResources()); + unset(self::$cache[$cache->getPath()]); } ); @@ -325,6 +326,7 @@ function (ConfigCacheInterface $cache) { $dumper = $this->getGeneratorDumperInstance(); $cache->write($dumper->dump(), $this->getRouteCollection()->getResources()); + unset(self::$cache[$cache->getPath()]); } ); diff --git a/symfony/translation/Command/XliffLintCommand.php b/symfony/translation/Command/XliffLintCommand.php index fb2b5f31c..0a0bc0ee6 100644 --- a/symfony/translation/Command/XliffLintCommand.php +++ b/symfony/translation/Command/XliffLintCommand.php @@ -42,7 +42,7 @@ class XliffLintCommand extends Command private $isReadableProvider; private $requireStrictFileNames; - public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = true) + public function __construct(?string $name = null, ?callable $directoryIteratorProvider = null, ?callable $isReadableProvider = null, bool $requireStrictFileNames = true) { parent::__construct($name); @@ -111,7 +111,7 @@ protected function execute(InputInterface $input, OutputInterface $output) return $this->display($io, $filesInfo); } - private function validate(string $content, string $file = null): array + private function validate(string $content, ?string $file = null): array { $errors = []; diff --git a/symfony/translation/DataCollector/TranslationDataCollector.php b/symfony/translation/DataCollector/TranslationDataCollector.php index 379130a44..12c1d04ae 100644 --- a/symfony/translation/DataCollector/TranslationDataCollector.php +++ b/symfony/translation/DataCollector/TranslationDataCollector.php @@ -48,7 +48,7 @@ public function lateCollect() /** * {@inheritdoc} */ - public function collect(Request $request, Response $response, \Throwable $exception = null) + public function collect(Request $request, Response $response, ?\Throwable $exception = null) { $this->data['locale'] = $this->translator->getLocale(); $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); diff --git a/symfony/translation/DataCollectorTranslator.php b/symfony/translation/DataCollectorTranslator.php index ea5a2dd5e..6de5e22a3 100644 --- a/symfony/translation/DataCollectorTranslator.php +++ b/symfony/translation/DataCollectorTranslator.php @@ -43,7 +43,7 @@ public function __construct(TranslatorInterface $translator) /** * {@inheritdoc} */ - public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) + public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null) { $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); $this->collectMessage($locale, $domain, $id, $trans, $parameters); @@ -70,7 +70,7 @@ public function getLocale() /** * {@inheritdoc} */ - public function getCatalogue(string $locale = null) + public function getCatalogue(?string $locale = null) { return $this->translator->getCatalogue($locale); } diff --git a/symfony/translation/Dumper/CsvFileDumper.php b/symfony/translation/Dumper/CsvFileDumper.php index 0c8589af8..3bb41f16b 100644 --- a/symfony/translation/Dumper/CsvFileDumper.php +++ b/symfony/translation/Dumper/CsvFileDumper.php @@ -31,7 +31,7 @@ public function formatCatalogue(MessageCatalogue $messages, string $domain, arra $handle = fopen('php://memory', 'r+'); foreach ($messages->all($domain) as $source => $target) { - fputcsv($handle, [$source, $target], $this->delimiter, $this->enclosure); + fputcsv($handle, [$source, $target], $this->delimiter, $this->enclosure, '\\'); } rewind($handle); diff --git a/symfony/translation/Dumper/XliffFileDumper.php b/symfony/translation/Dumper/XliffFileDumper.php index f7dbdcddf..a480b3f23 100644 --- a/symfony/translation/Dumper/XliffFileDumper.php +++ b/symfony/translation/Dumper/XliffFileDumper.php @@ -196,7 +196,7 @@ private function dumpXliff2(string $defaultLocale, MessageCatalogue $messages, ? return $dom->saveXML(); } - private function hasMetadataArrayInfo(string $key, array $metadata = null): bool + private function hasMetadataArrayInfo(string $key, ?array $metadata = null): bool { return is_iterable($metadata[$key] ?? null); } diff --git a/symfony/translation/Exception/IncompleteDsnException.php b/symfony/translation/Exception/IncompleteDsnException.php index cb0ce027e..b304bde01 100644 --- a/symfony/translation/Exception/IncompleteDsnException.php +++ b/symfony/translation/Exception/IncompleteDsnException.php @@ -13,7 +13,7 @@ class IncompleteDsnException extends InvalidArgumentException { - public function __construct(string $message, string $dsn = null, \Throwable $previous = null) + public function __construct(string $message, ?string $dsn = null, ?\Throwable $previous = null) { if ($dsn) { $message = sprintf('Invalid "%s" provider DSN: ', $dsn).$message; diff --git a/symfony/translation/Exception/MissingRequiredOptionException.php b/symfony/translation/Exception/MissingRequiredOptionException.php index 2b5f80806..46152e254 100644 --- a/symfony/translation/Exception/MissingRequiredOptionException.php +++ b/symfony/translation/Exception/MissingRequiredOptionException.php @@ -16,7 +16,7 @@ */ class MissingRequiredOptionException extends IncompleteDsnException { - public function __construct(string $option, string $dsn = null, \Throwable $previous = null) + public function __construct(string $option, ?string $dsn = null, ?\Throwable $previous = null) { $message = sprintf('The option "%s" is required but missing.', $option); diff --git a/symfony/translation/Exception/ProviderException.php b/symfony/translation/Exception/ProviderException.php index 571920da3..8b909fe27 100644 --- a/symfony/translation/Exception/ProviderException.php +++ b/symfony/translation/Exception/ProviderException.php @@ -21,7 +21,7 @@ class ProviderException extends RuntimeException implements ProviderExceptionInt private $response; private $debug; - public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null) + public function __construct(string $message, ResponseInterface $response, int $code = 0, ?\Exception $previous = null) { $this->response = $response; $this->debug = $response->getInfo('debug') ?? ''; diff --git a/symfony/translation/Exception/UnsupportedSchemeException.php b/symfony/translation/Exception/UnsupportedSchemeException.php index 7fbaa8f04..800c4cdb6 100644 --- a/symfony/translation/Exception/UnsupportedSchemeException.php +++ b/symfony/translation/Exception/UnsupportedSchemeException.php @@ -31,7 +31,7 @@ class UnsupportedSchemeException extends LogicException ], ]; - public function __construct(Dsn $dsn, string $name = null, array $supported = []) + public function __construct(Dsn $dsn, ?string $name = null, array $supported = []) { $provider = $dsn->getScheme(); if (false !== $pos = strpos($provider, '+')) { diff --git a/symfony/translation/Extractor/PhpStringTokenParser.php b/symfony/translation/Extractor/PhpStringTokenParser.php index d114cc738..c0699461d 100644 --- a/symfony/translation/Extractor/PhpStringTokenParser.php +++ b/symfony/translation/Extractor/PhpStringTokenParser.php @@ -93,7 +93,7 @@ public static function parse(string $str) * * @return string */ - public static function parseEscapeSequences(string $str, string $quote = null) + public static function parseEscapeSequences(string $str, ?string $quote = null) { if (null !== $quote) { $str = str_replace('\\'.$quote, $quote, $str); diff --git a/symfony/translation/Formatter/MessageFormatter.php b/symfony/translation/Formatter/MessageFormatter.php index 040796483..3449a84a5 100644 --- a/symfony/translation/Formatter/MessageFormatter.php +++ b/symfony/translation/Formatter/MessageFormatter.php @@ -28,7 +28,7 @@ class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterf /** * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization */ - public function __construct(TranslatorInterface $translator = null, IntlFormatterInterface $intlFormatter = null) + public function __construct(?TranslatorInterface $translator = null, ?IntlFormatterInterface $intlFormatter = null) { $this->translator = $translator ?? new IdentityTranslator(); $this->intlFormatter = $intlFormatter ?? new IntlFormatter(); diff --git a/symfony/translation/Loader/CsvFileLoader.php b/symfony/translation/Loader/CsvFileLoader.php index 8d5d4db9a..0cf05731b 100644 --- a/symfony/translation/Loader/CsvFileLoader.php +++ b/symfony/translation/Loader/CsvFileLoader.php @@ -22,7 +22,7 @@ class CsvFileLoader extends FileLoader { private $delimiter = ';'; private $enclosure = '"'; - private $escape = '\\'; + private $escape = ''; /** * {@inheritdoc} @@ -38,7 +38,7 @@ protected function loadResource(string $resource) } $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY); - $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape); + $file->setCsvControl($this->delimiter, $this->enclosure, '' === $this->escape && \PHP_VERSION_ID < 70400 ? '\\' : $this->escape); foreach ($file as $data) { if (false === $data) { @@ -56,10 +56,10 @@ protected function loadResource(string $resource) /** * Sets the delimiter, enclosure, and escape character for CSV. */ - public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '\\') + public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '') { $this->delimiter = $delimiter; $this->enclosure = $enclosure; - $this->escape = $escape; + $this->escape = '' === $escape && \PHP_VERSION_ID < 70400 ? '\\' : $escape; } } diff --git a/symfony/translation/Loader/IcuResFileLoader.php b/symfony/translation/Loader/IcuResFileLoader.php index 6b7834e67..88e133738 100644 --- a/symfony/translation/Loader/IcuResFileLoader.php +++ b/symfony/translation/Loader/IcuResFileLoader.php @@ -75,7 +75,7 @@ public function load($resource, string $locale, string $domain = 'messages') * * @return array */ - protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null) + protected function flatten(\ResourceBundle $rb, array &$messages = [], ?string $path = null) { foreach ($rb as $key => $value) { $nodePath = $path ? $path.'.'.$key : $key; diff --git a/symfony/translation/Loader/XliffFileLoader.php b/symfony/translation/Loader/XliffFileLoader.php index 5c9794a54..f4d239619 100644 --- a/symfony/translation/Loader/XliffFileLoader.php +++ b/symfony/translation/Loader/XliffFileLoader.php @@ -111,12 +111,20 @@ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, s continue; } - $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; + $source = (string) (isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source); + + if (isset($translation->target) + && 'needs-translation' === (string) $translation->target->attributes()['state'] + && \in_array((string) $translation->target, [$source, (string) $translation->source], true) + ) { + continue; + } + // If the xlf file has another encoding specified, try to convert it because // simple_xml will always return utf-8 encoded values $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding); - $catalogue->set((string) $source, $target, $domain); + $catalogue->set($source, $target, $domain); $metadata = [ 'source' => (string) $translation->source, @@ -139,7 +147,7 @@ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, s $metadata['id'] = (string) $attributes['id']; } - $catalogue->setMetadata((string) $source, $metadata, $domain); + $catalogue->setMetadata($source, $metadata, $domain); } } } @@ -190,7 +198,7 @@ private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, s /** * Convert a UTF8 string to the specified encoding. */ - private function utf8ToCharset(string $content, string $encoding = null): string + private function utf8ToCharset(string $content, ?string $encoding = null): string { if ('UTF-8' !== $encoding && !empty($encoding)) { return mb_convert_encoding($content, $encoding, 'UTF-8'); @@ -199,7 +207,7 @@ private function utf8ToCharset(string $content, string $encoding = null): string return $content; } - private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array + private function parseNotesMetadata(?\SimpleXMLElement $noteElement = null, ?string $encoding = null): array { $notes = []; diff --git a/symfony/translation/LoggingTranslator.php b/symfony/translation/LoggingTranslator.php index 6ccd48289..d0932b667 100644 --- a/symfony/translation/LoggingTranslator.php +++ b/symfony/translation/LoggingTranslator.php @@ -40,7 +40,7 @@ public function __construct(TranslatorInterface $translator, LoggerInterface $lo /** * {@inheritdoc} */ - public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) + public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null) { $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); $this->log($id, $domain, $locale); @@ -73,7 +73,7 @@ public function getLocale() /** * {@inheritdoc} */ - public function getCatalogue(string $locale = null) + public function getCatalogue(?string $locale = null) { return $this->translator->getCatalogue($locale); } diff --git a/symfony/translation/MessageCatalogue.php b/symfony/translation/MessageCatalogue.php index 9da3b7f00..2e00b645e 100644 --- a/symfony/translation/MessageCatalogue.php +++ b/symfony/translation/MessageCatalogue.php @@ -63,7 +63,7 @@ public function getDomains() /** * {@inheritdoc} */ - public function all(string $domain = null) + public function all(?string $domain = null) { if (null !== $domain) { // skip messages merge if intl-icu requested explicitly diff --git a/symfony/translation/MessageCatalogueInterface.php b/symfony/translation/MessageCatalogueInterface.php index 965bf008f..d532a1e48 100644 --- a/symfony/translation/MessageCatalogueInterface.php +++ b/symfony/translation/MessageCatalogueInterface.php @@ -45,7 +45,7 @@ public function getDomains(); * * @return array */ - public function all(string $domain = null); + public function all(?string $domain = null); /** * Sets a message translation. diff --git a/symfony/translation/Provider/AbstractProviderFactory.php b/symfony/translation/Provider/AbstractProviderFactory.php index 17442fde8..fdfeb8ce3 100644 --- a/symfony/translation/Provider/AbstractProviderFactory.php +++ b/symfony/translation/Provider/AbstractProviderFactory.php @@ -28,7 +28,7 @@ abstract protected function getSupportedSchemes(): array; protected function getUser(Dsn $dsn): string { if (null === $user = $dsn->getUser()) { - throw new IncompleteDsnException('User is not set.', $dsn->getOriginalDsn()); + throw new IncompleteDsnException('User is not set.', $dsn->getScheme().'://'.$dsn->getHost()); } return $user; diff --git a/symfony/translation/Provider/Dsn.php b/symfony/translation/Provider/Dsn.php index 820cabfb3..d29b202d1 100644 --- a/symfony/translation/Provider/Dsn.php +++ b/symfony/translation/Provider/Dsn.php @@ -33,25 +33,25 @@ public function __construct(string $dsn) { $this->originalDsn = $dsn; - if (false === $parsedDsn = parse_url($dsn)) { - throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN is invalid.', $dsn)); + if (false === $params = parse_url($dsn)) { + throw new InvalidArgumentException('The translation provider DSN is invalid.'); } - if (!isset($parsedDsn['scheme'])) { - throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN must contain a scheme.', $dsn)); + if (!isset($params['scheme'])) { + throw new InvalidArgumentException('The translation provider DSN must contain a scheme.'); } - $this->scheme = $parsedDsn['scheme']; + $this->scheme = $params['scheme']; - if (!isset($parsedDsn['host'])) { - throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN must contain a host (use "default" by default).', $dsn)); + if (!isset($params['host'])) { + throw new InvalidArgumentException('The translation provider DSN must contain a host (use "default" by default).'); } - $this->host = $parsedDsn['host']; + $this->host = $params['host']; - $this->user = '' !== ($parsedDsn['user'] ?? '') ? urldecode($parsedDsn['user']) : null; - $this->password = '' !== ($parsedDsn['pass'] ?? '') ? urldecode($parsedDsn['pass']) : null; - $this->port = $parsedDsn['port'] ?? null; - $this->path = $parsedDsn['path'] ?? null; - parse_str($parsedDsn['query'] ?? '', $this->options); + $this->user = '' !== ($params['user'] ?? '') ? rawurldecode($params['user']) : null; + $this->password = '' !== ($params['pass'] ?? '') ? rawurldecode($params['pass']) : null; + $this->port = $params['port'] ?? null; + $this->path = $params['path'] ?? null; + parse_str($params['query'] ?? '', $this->options); } public function getScheme(): string @@ -74,7 +74,7 @@ public function getPassword(): ?string return $this->password; } - public function getPort(int $default = null): ?int + public function getPort(?int $default = null): ?int { return $this->port ?? $default; } diff --git a/symfony/translation/PseudoLocalizationTranslator.php b/symfony/translation/PseudoLocalizationTranslator.php index c769bdad0..5396eb546 100644 --- a/symfony/translation/PseudoLocalizationTranslator.php +++ b/symfony/translation/PseudoLocalizationTranslator.php @@ -86,7 +86,7 @@ public function __construct(TranslatorInterface $translator, array $options = [] /** * {@inheritdoc} */ - public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string + public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string { $trans = ''; $visibleText = ''; @@ -123,7 +123,7 @@ private function getParts(string $originalTrans): array return [[true, true, $originalTrans]]; } - $html = mb_encode_numericentity($originalTrans, [0x80, 0xFFFF, 0, 0xFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8'); + $html = mb_encode_numericentity($originalTrans, [0x80, 0x10FFFF, 0, 0x1FFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8'); $useInternalErrors = libxml_use_internal_errors(true); diff --git a/symfony/translation/Resources/data/parents.json b/symfony/translation/Resources/data/parents.json index 32a33cdaf..24d4d119e 100644 --- a/symfony/translation/Resources/data/parents.json +++ b/symfony/translation/Resources/data/parents.json @@ -35,6 +35,7 @@ "en_GM": "en_001", "en_GY": "en_001", "en_HK": "en_001", + "en_ID": "en_001", "en_IE": "en_001", "en_IL": "en_001", "en_IM": "en_001", diff --git a/symfony/translation/Resources/functions.php b/symfony/translation/Resources/functions.php index 901d2f87e..0d2a037a2 100644 --- a/symfony/translation/Resources/functions.php +++ b/symfony/translation/Resources/functions.php @@ -15,7 +15,7 @@ /** * @author Nate Wiebe */ - function t(string $message, array $parameters = [], string $domain = null): TranslatableMessage + function t(string $message, array $parameters = [], ?string $domain = null): TranslatableMessage { return new TranslatableMessage($message, $parameters, $domain); } diff --git a/symfony/translation/TranslatableMessage.php b/symfony/translation/TranslatableMessage.php index 282d289c0..4e53d6079 100644 --- a/symfony/translation/TranslatableMessage.php +++ b/symfony/translation/TranslatableMessage.php @@ -23,7 +23,7 @@ class TranslatableMessage implements TranslatableInterface private $parameters; private $domain; - public function __construct(string $message, array $parameters = [], string $domain = null) + public function __construct(string $message, array $parameters = [], ?string $domain = null) { $this->message = $message; $this->parameters = $parameters; @@ -50,7 +50,7 @@ public function getDomain(): ?string return $this->domain; } - public function trans(TranslatorInterface $translator, string $locale = null): string + public function trans(TranslatorInterface $translator, ?string $locale = null): string { return $translator->trans($this->getMessage(), array_map( static function ($parameter) use ($translator, $locale) { diff --git a/symfony/translation/Translator.php b/symfony/translation/Translator.php index dc0626093..9270e51dc 100644 --- a/symfony/translation/Translator.php +++ b/symfony/translation/Translator.php @@ -89,7 +89,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA /** * @throws InvalidArgumentException If a locale contains invalid characters */ - public function __construct(string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false, array $cacheVary = []) + public function __construct(string $locale, ?MessageFormatterInterface $formatter = null, ?string $cacheDir = null, bool $debug = false, array $cacheVary = []) { $this->setLocale($locale); @@ -127,7 +127,7 @@ public function addLoader(string $format, LoaderInterface $loader) * * @throws InvalidArgumentException If the locale contains invalid characters */ - public function addResource(string $format, $resource, string $locale, string $domain = null) + public function addResource(string $format, $resource, string $locale, ?string $domain = null) { if (null === $domain) { $domain = 'messages'; @@ -194,7 +194,7 @@ public function getFallbackLocales(): array /** * {@inheritdoc} */ - public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) + public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null) { if (null === $id || '' === $id) { return ''; @@ -229,7 +229,7 @@ public function trans(?string $id, array $parameters = [], string $domain = null /** * {@inheritdoc} */ - public function getCatalogue(string $locale = null) + public function getCatalogue(?string $locale = null) { if (!$locale) { $locale = $this->getLocale(); diff --git a/symfony/translation/TranslatorBag.php b/symfony/translation/TranslatorBag.php index 6a4df3c3c..c8ae1a2fa 100644 --- a/symfony/translation/TranslatorBag.php +++ b/symfony/translation/TranslatorBag.php @@ -38,7 +38,7 @@ public function addBag(TranslatorBagInterface $bag): void /** * {@inheritdoc} */ - public function getCatalogue(string $locale = null): MessageCatalogueInterface + public function getCatalogue(?string $locale = null): MessageCatalogueInterface { if (null === $locale || !isset($this->catalogues[$locale])) { $this->catalogues[$locale] = new MessageCatalogue($locale); diff --git a/symfony/translation/TranslatorBagInterface.php b/symfony/translation/TranslatorBagInterface.php index 422897735..3fc3bda39 100644 --- a/symfony/translation/TranslatorBagInterface.php +++ b/symfony/translation/TranslatorBagInterface.php @@ -31,5 +31,5 @@ interface TranslatorBagInterface * * @throws InvalidArgumentException If the locale contains invalid characters */ - public function getCatalogue(string $locale = null); + public function getCatalogue(?string $locale = null); } diff --git a/symfony/translation/Util/ArrayConverter.php b/symfony/translation/Util/ArrayConverter.php index f69c2e3c6..cbab0c590 100644 --- a/symfony/translation/Util/ArrayConverter.php +++ b/symfony/translation/Util/ArrayConverter.php @@ -38,7 +38,7 @@ public static function expandToTree(array $messages) $tree = []; foreach ($messages as $id => $value) { - $referenceToElement = &self::getElementByPath($tree, explode('.', $id)); + $referenceToElement = &self::getElementByPath($tree, self::getKeyParts($id)); $referenceToElement = $value; @@ -65,6 +65,7 @@ private static function &getElementByPath(array &$tree, array $parts) $elem = &$elem[implode('.', \array_slice($parts, $i))]; break; } + $parentOfElem = &$elem; $elem = &$elem[$part]; } @@ -96,4 +97,48 @@ private static function cancelExpand(array &$tree, string $prefix, array $node) } } } + + /** + * @return string[] + */ + private static function getKeyParts(string $key): array + { + $parts = explode('.', $key); + $partsCount = \count($parts); + + $result = []; + $buffer = ''; + + foreach ($parts as $index => $part) { + if (0 === $index && '' === $part) { + $buffer = '.'; + + continue; + } + + if ($index === $partsCount - 1 && '' === $part) { + $buffer .= '.'; + $result[] = $buffer; + + continue; + } + + if (isset($parts[$index + 1]) && '' === $parts[$index + 1]) { + $buffer .= $part; + + continue; + } + + if ($buffer) { + $result[] = $buffer.$part; + $buffer = ''; + + continue; + } + + $result[] = $part; + } + + return $result; + } } From 50ff8725a7919420d1bb6cfb5ba890a2c96516f0 Mon Sep 17 00:00:00 2001 From: nextcloud-command Date: Wed, 29 Jan 2025 15:41:38 +0000 Subject: [PATCH 2/2] chore(autoloader): Dump autoloader Signed-off-by: nextcloud-command --- composer/InstalledVersions.php | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/composer/InstalledVersions.php b/composer/InstalledVersions.php index 51e734a77..6d29bff66 100644 --- a/composer/InstalledVersions.php +++ b/composer/InstalledVersions.php @@ -32,6 +32,11 @@ class InstalledVersions */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +314,12 @@ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; } /** @@ -322,19 +333,27 @@ private static function getInstalled() } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = strtr(__DIR__, '\\', '/'); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -350,7 +369,7 @@ private static function getInstalled() } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; }