From 1d60b10a43883b32a4dbb3c6ca2cf90dc19bce98 Mon Sep 17 00:00:00 2001 From: Michael McAndrew Date: Wed, 18 Mar 2020 15:58:29 +0000 Subject: [PATCH] upgrading vendor directory; releasing beta.5 --- composer.lock | 387 +- info.xml | 4 +- vendor/bin/var-dump-server | 1 + vendor/botman/botman/README.md | 15 + vendor/botman/botman/composer.json | 2 +- vendor/botman/botman/src/BotMan.php | 15 +- .../src/Commands/ConversationManager.php | 3 + .../src/Messages/Attachments/Contact.php | 118 + .../Messages/Conversations/Conversation.php | 34 + .../src/Messages/Incoming/IncomingMessage.php | 24 + vendor/botman/botman/src/Middleware/ApiAi.php | 11 + .../src/Events/MessagingAccountLinking.php | 16 + .../Extensions/AbstractAirlineTemplate.php | 80 + .../Airline/AbstractAirlineFlightInfo.php | 70 + .../src/Extensions/Airline/AirlineAirport.php | 98 + .../Airline/AirlineBoardingPass.php | 292 ++ .../Airline/AirlineExtendedFlightInfo.php | 124 + .../Extensions/Airline/AirlineFlightInfo.php | 23 + .../Airline/AirlineFlightSchedule.php | 89 + .../Airline/AirlinePassengerInfo.php | 80 + .../Airline/AirlinePassengerSegmentInfo.php | 119 + .../AirlineBoardingPassTemplate.php | 77 + .../src/Extensions/AirlineCheckInTemplate.php | 109 + .../Extensions/AirlineItineraryTemplate.php | 219 ++ .../src/Extensions/AirlineUpdateTemplate.php | 111 + .../driver-facebook/src/FacebookDriver.php | 13 +- .../src/Interfaces/Airline.php | 26 + vendor/composer/ClassLoader.php | 2 +- vendor/composer/autoload_classmap.php | 7 - vendor/composer/autoload_files.php | 5 +- vendor/composer/autoload_psr4.php | 4 +- vendor/composer/autoload_static.php | 32 +- vendor/composer/installed.json | 419 ++- vendor/opis/closure/CHANGELOG.md | 48 +- vendor/opis/closure/LICENSE | 2 +- vendor/opis/closure/README.md | 2 +- vendor/opis/closure/autoload.php | 2 +- vendor/opis/closure/composer.json | 4 +- vendor/opis/closure/functions.php | 2 +- vendor/opis/closure/src/Analyzer.php | 2 +- vendor/opis/closure/src/ClosureContext.php | 2 +- vendor/opis/closure/src/ClosureScope.php | 2 +- vendor/opis/closure/src/ClosureStream.php | 7 +- vendor/opis/closure/src/ISecurityProvider.php | 2 +- vendor/opis/closure/src/ReflectionClosure.php | 83 +- vendor/opis/closure/src/SecurityException.php | 2 +- vendor/opis/closure/src/SecurityProvider.php | 2 +- vendor/opis/closure/src/SelfReference.php | 2 +- .../opis/closure/src/SerializableClosure.php | 70 +- vendor/paragonie/random_compat/build-phar.sh | 5 - vendor/paragonie/random_compat/composer.json | 34 - .../dist/random_compat.phar.pubkey | 5 - .../dist/random_compat.phar.pubkey.asc | 11 - vendor/paragonie/random_compat/lib/random.php | 32 - .../random_compat/other/build_phar.php | 57 - .../random_compat/psalm-autoload.php | 9 - vendor/paragonie/random_compat/psalm.xml | 19 - vendor/react/cache/.travis.yml | 13 +- vendor/react/cache/CHANGELOG.md | 25 + vendor/react/cache/README.md | 155 +- vendor/react/cache/src/ArrayCache.php | 99 +- vendor/react/cache/src/CacheInterface.php | 114 + vendor/react/cache/tests/ArrayCacheTest.php | 126 + vendor/react/dns/.travis.yml | 5 +- vendor/react/dns/CHANGELOG.md | 85 + vendor/react/dns/README.md | 162 +- vendor/react/dns/composer.json | 11 +- vendor/react/dns/examples/12-all-types.php | 3 + .../dns/examples/91-query-a-and-aaaa.php | 6 +- vendor/react/dns/examples/92-query-any.php | 21 +- vendor/react/dns/phpunit.xml.dist | 1 - vendor/react/dns/src/BadServerException.php | 2 +- vendor/react/dns/src/Config/Config.php | 2 +- .../dns/src/Config/FilesystemFactory.php | 73 - vendor/react/dns/src/Config/HostsFile.php | 2 + vendor/react/dns/src/Model/HeaderBag.php | 59 - vendor/react/dns/src/Model/Message.php | 129 +- vendor/react/dns/src/Model/Record.php | 38 +- .../react/dns/src/Protocol/BinaryDumper.php | 171 +- vendor/react/dns/src/Protocol/Parser.php | 234 +- vendor/react/dns/src/Query/CachedExecutor.php | 55 - .../react/dns/src/Query/CachingExecutor.php | 88 + .../dns/src/Query/CancellationException.php | 2 +- vendor/react/dns/src/Query/CoopExecutor.php | 92 + vendor/react/dns/src/Query/Executor.php | 160 - .../react/dns/src/Query/ExecutorInterface.php | 37 +- .../react/dns/src/Query/HostsFileExecutor.php | 8 +- vendor/react/dns/src/Query/Query.php | 37 +- vendor/react/dns/src/Query/RecordBag.php | 26 - vendor/react/dns/src/Query/RecordCache.php | 123 - vendor/react/dns/src/Query/RetryExecutor.php | 14 +- .../src/Query/SelectiveTransportExecutor.php | 85 + .../dns/src/Query/TcpTransportExecutor.php | 346 ++ .../react/dns/src/Query/TimeoutException.php | 2 +- .../react/dns/src/Query/TimeoutExecutor.php | 6 +- .../dns/src/Query/UdpTransportExecutor.php | 69 +- .../react/dns/src/RecordNotFoundException.php | 2 +- vendor/react/dns/src/Resolver/Factory.php | 88 +- vendor/react/dns/src/Resolver/Resolver.php | 115 +- .../dns/src/Resolver/ResolverInterface.php | 94 + vendor/react/dns/tests/Config/ConfigTest.php | 6 +- .../tests/Config/FilesystemFactoryTest.php | 70 - .../dns/tests/FunctionalResolverTest.php | 111 + vendor/react/dns/tests/Model/MessageTest.php | 14 +- .../dns/tests/Protocol/BinaryDumperTest.php | 299 +- .../react/dns/tests/Protocol/ParserTest.php | 605 ++-- .../dns/tests/Query/CachedExecutorTest.php | 100 - .../dns/tests/Query/CachingExecutorTest.php | 183 + .../dns/tests/Query/CoopExecutorTest.php | 233 ++ vendor/react/dns/tests/Query/ExecutorTest.php | 308 -- .../dns/tests/Query/HostsFileExecutorTest.php | 26 +- .../react/dns/tests/Query/RecordBagTest.php | 83 - .../react/dns/tests/Query/RecordCacheTest.php | 193 - .../dns/tests/Query/RetryExecutorTest.php | 79 +- .../Query/SelectiveTransportExecutorTest.php | 220 ++ .../tests/Query/TcpTransportExecutorTest.php | 811 +++++ .../dns/tests/Query/TimeoutExecutorTest.php | 20 +- .../tests/Query/UdpTransportExecutorTest.php | 152 +- .../react/dns/tests/Resolver/FactoryTest.php | 162 +- .../dns/tests/Resolver/ResolveAliasesTest.php | 26 +- .../react/dns/tests/Resolver/ResolverTest.php | 97 +- vendor/react/event-loop/.gitignore | 3 - vendor/react/event-loop/.travis.yml | 39 - vendor/react/event-loop/CHANGELOG.md | 38 + vendor/react/event-loop/README.md | 30 +- vendor/react/event-loop/composer.json | 5 +- .../react/event-loop/examples/01-timers.php | 15 - .../react/event-loop/examples/02-periodic.php | 16 - vendor/react/event-loop/examples/03-ticks.php | 15 - .../react/event-loop/examples/04-signals.php | 19 - .../event-loop/examples/11-consume-stdin.php | 30 - .../event-loop/examples/12-generate-yes.php | 41 - .../examples/13-http-client-blocking.php | 35 - .../examples/14-http-client-async.php | 63 - .../event-loop/examples/21-http-server.php | 36 - .../examples/91-benchmark-ticks.php | 15 - .../examples/92-benchmark-timers.php | 15 - .../examples/93-benchmark-ticks-delay.php | 22 - .../examples/94-benchmark-timers-delay.php | 22 - .../examples/95-benchmark-memory.php | 67 - vendor/react/event-loop/phpunit.xml.dist | 25 - vendor/react/event-loop/src/ExtEventLoop.php | 21 +- vendor/react/event-loop/src/ExtUvLoop.php | 341 ++ vendor/react/event-loop/src/Factory.php | 9 +- vendor/react/event-loop/src/LoopInterface.php | 8 +- .../react/event-loop/src/StreamSelectLoop.php | 65 +- vendor/react/event-loop/src/Timer/Timers.php | 11 +- .../event-loop/tests/AbstractLoopTest.php | 616 ---- .../react/event-loop/tests/CallableStub.php | 10 - .../react/event-loop/tests/ExtEvLoopTest.php | 17 - .../event-loop/tests/ExtEventLoopTest.php | 84 - .../event-loop/tests/ExtLibevLoopTest.php | 22 - .../event-loop/tests/ExtLibeventLoopTest.php | 58 - .../event-loop/tests/SignalsHandlerTest.php | 55 - .../event-loop/tests/StreamSelectLoopTest.php | 148 - vendor/react/event-loop/tests/TestCase.php | 53 - .../tests/Timer/AbstractTimerTest.php | 122 - .../event-loop/tests/Timer/ExtEvTimerTest.php | 17 - .../tests/Timer/ExtEventTimerTest.php | 17 - .../tests/Timer/ExtLibevTimerTest.php | 17 - .../tests/Timer/ExtLibeventTimerTest.php | 17 - .../tests/Timer/StreamSelectTimerTest.php | 13 - .../event-loop/tests/Timer/TimersTest.php | 40 - vendor/react/event-loop/tests/bootstrap.php | 9 - vendor/react/event-loop/travis-init.sh | 42 - vendor/react/promise-timer/CHANGELOG.md | 8 + vendor/react/promise-timer/README.md | 2 +- vendor/react/promise-timer/composer.json | 2 +- .../promise-timer/src/functions_include.php | 7 + vendor/react/socket/.gitignore | 2 - vendor/react/socket/.travis.yml | 48 - vendor/react/socket/CHANGELOG.md | 92 + vendor/react/socket/README.md | 300 +- vendor/react/socket/composer.json | 11 +- .../react/socket/examples/01-echo-server.php | 42 - .../react/socket/examples/02-chat-server.php | 59 - .../react/socket/examples/03-http-server.php | 57 - .../react/socket/examples/11-http-client.php | 36 - .../react/socket/examples/12-https-client.php | 36 - .../socket/examples/21-netcat-client.php | 68 - .../react/socket/examples/22-http-client.php | 60 - .../socket/examples/91-benchmark-server.php | 60 - .../examples/99-generate-self-signed.php | 31 - vendor/react/socket/examples/localhost.pem | 49 - .../socket/examples/localhost_swordfish.pem | 51 - vendor/react/socket/phpunit.xml.dist | 25 - vendor/react/socket/src/Connection.php | 42 +- vendor/react/socket/src/Connector.php | 37 +- .../react/socket/src/ConnectorInterface.php | 2 +- vendor/react/socket/src/DnsConnector.php | 135 +- vendor/react/socket/src/FixedUriConnector.php | 4 +- .../src/HappyEyeBallsConnectionBuilder.php | 296 ++ .../socket/src/HappyEyeBallsConnector.php | 53 + vendor/react/socket/src/LimitingServer.php | 24 +- vendor/react/socket/src/SecureConnector.php | 42 +- vendor/react/socket/src/SecureServer.php | 39 +- vendor/react/socket/src/Server.php | 4 +- vendor/react/socket/src/ServerInterface.php | 2 +- vendor/react/socket/src/StreamEncryption.php | 84 +- vendor/react/socket/src/TcpConnector.php | 57 +- vendor/react/socket/src/TcpServer.php | 70 +- vendor/react/socket/src/TimeoutConnector.php | 27 +- vendor/react/socket/src/UnixConnector.php | 10 +- vendor/react/socket/src/UnixServer.php | 43 +- vendor/react/socket/tests/ConnectionTest.php | 47 - vendor/react/socket/tests/ConnectorTest.php | 128 - .../react/socket/tests/DnsConnectorTest.php | 111 - .../socket/tests/FixedUriConnectorTest.php | 19 - .../socket/tests/FunctionalConnectorTest.php | 32 - .../tests/FunctionalSecureServerTest.php | 438 --- .../socket/tests/FunctionalTcpServerTest.php | 324 -- vendor/react/socket/tests/IntegrationTest.php | 328 -- .../react/socket/tests/LimitingServerTest.php | 195 - .../socket/tests/SecureConnectorTest.php | 74 - .../socket/tests/SecureIntegrationTest.php | 204 -- .../react/socket/tests/SecureServerTest.php | 105 - vendor/react/socket/tests/ServerTest.php | 173 - .../react/socket/tests/Stub/CallableStub.php | 10 - .../socket/tests/Stub/ConnectionStub.php | 63 - vendor/react/socket/tests/Stub/ServerStub.php | 18 - .../react/socket/tests/TcpConnectorTest.php | 260 -- vendor/react/socket/tests/TcpServerTest.php | 285 -- vendor/react/socket/tests/TestCase.php | 101 - .../socket/tests/TimeoutConnectorTest.php | 103 - .../react/socket/tests/UnixConnectorTest.php | 64 - vendor/react/socket/tests/UnixServerTest.php | 283 -- vendor/symfony/http-foundation/.gitattributes | 3 + vendor/symfony/http-foundation/.gitignore | 3 - .../symfony/http-foundation/AcceptHeader.php | 17 +- .../http-foundation/AcceptHeaderItem.php | 34 +- .../symfony/http-foundation/ApacheRequest.php | 4 + .../http-foundation/BinaryFileResponse.php | 48 +- vendor/symfony/http-foundation/CHANGELOG.md | 82 +- vendor/symfony/http-foundation/Cookie.php | 98 +- .../ExpressionRequestMatcher.php | 4 +- .../File/Exception/AccessDeniedException.php | 5 +- .../Exception/CannotWriteFileException.php | 21 + .../File/Exception/ExtensionFileException.php | 21 + .../File/Exception/FileNotFoundException.php | 5 +- .../File/Exception/FormSizeFileException.php | 21 + .../File/Exception/IniSizeFileException.php | 21 + .../File/Exception/NoFileException.php | 21 + .../File/Exception/NoTmpDirFileException.php | 21 + .../File/Exception/PartialFileException.php | 21 + .../Exception/UnexpectedTypeException.php | 2 +- vendor/symfony/http-foundation/File/File.php | 29 +- .../File/MimeType/ExtensionGuesser.php | 10 +- .../MimeType/ExtensionGuesserInterface.php | 4 + .../MimeType/FileBinaryMimeTypeGuesser.php | 17 +- .../File/MimeType/FileinfoMimeTypeGuesser.php | 13 +- .../MimeType/MimeTypeExtensionGuesser.php | 38 +- .../File/MimeType/MimeTypeGuesser.php | 7 +- .../MimeType/MimeTypeGuesserInterface.php | 5 +- .../http-foundation/File/UploadedFile.php | 78 +- vendor/symfony/http-foundation/FileBag.php | 28 +- vendor/symfony/http-foundation/HeaderBag.php | 98 +- .../symfony/http-foundation/HeaderUtils.php | 224 ++ vendor/symfony/http-foundation/IpUtils.php | 36 +- .../symfony/http-foundation/JsonResponse.php | 63 +- vendor/symfony/http-foundation/LICENSE | 2 +- .../symfony/http-foundation/ParameterBag.php | 25 +- vendor/symfony/http-foundation/README.md | 2 +- .../http-foundation/RedirectResponse.php | 17 +- vendor/symfony/http-foundation/Request.php | 522 ++- .../http-foundation/RequestMatcher.php | 39 +- .../symfony/http-foundation/RequestStack.php | 8 +- vendor/symfony/http-foundation/Response.php | 383 +- .../http-foundation/ResponseHeaderBag.php | 111 +- vendor/symfony/http-foundation/ServerBag.php | 17 +- .../Session/Attribute/AttributeBag.php | 14 +- .../Attribute/AttributeBagInterface.php | 7 +- .../Attribute/NamespacedAttributeBag.php | 16 +- .../Session/Flash/AutoExpireFlashBag.php | 18 +- .../Session/Flash/FlashBag.php | 12 +- .../Session/Flash/FlashBagInterface.php | 12 +- .../http-foundation/Session/Session.php | 28 +- .../Session/SessionBagProxy.php | 18 +- .../Session/SessionInterface.php | 16 +- .../http-foundation/Session/SessionUtils.php | 59 + .../Handler/AbstractSessionHandler.php | 68 +- .../Handler/MemcacheSessionHandler.php | 118 - .../Handler/MemcachedSessionHandler.php | 17 +- .../Handler/MigratingSessionHandler.php | 124 + .../Storage/Handler/MongoDbSessionHandler.php | 138 +- .../Handler/NativeFileSessionHandler.php | 6 +- .../Storage/Handler/NativeSessionHandler.php | 24 - .../Storage/Handler/NullSessionHandler.php | 8 +- .../Storage/Handler/PdoSessionHandler.php | 143 +- .../Storage/Handler/RedisSessionHandler.php | 120 + .../Storage/Handler/SessionHandlerFactory.php | 85 + .../Storage/Handler/StrictSessionHandler.php | 10 +- .../Handler/WriteCheckSessionHandler.php | 92 - .../Session/Storage/MetadataBag.php | 6 +- .../Storage/MockArraySessionStorage.php | 16 +- .../Storage/MockFileSessionStorage.php | 19 +- .../Session/Storage/NativeSessionStorage.php | 102 +- .../Storage/PhpBridgeSessionStorage.php | 9 +- .../Session/Storage/Proxy/AbstractProxy.php | 4 +- .../Session/Storage/Proxy/NativeProxy.php | 40 - .../Storage/Proxy/SessionHandlerProxy.php | 16 +- .../Storage/SessionStorageInterface.php | 2 +- .../http-foundation/StreamedResponse.php | 10 +- .../Constraint/RequestAttributeValueSame.php | 55 + .../Constraint/ResponseCookieValueSame.php | 85 + .../Test/Constraint/ResponseHasCookie.php | 77 + .../Test/Constraint/ResponseHasHeader.php | 53 + .../Test/Constraint/ResponseHeaderSame.php | 55 + .../Test/Constraint/ResponseIsRedirected.php | 56 + .../Test/Constraint/ResponseIsSuccessful.php | 56 + .../Constraint/ResponseStatusCodeSame.php | 63 + .../Tests/AcceptHeaderItemTest.php | 113 - .../Tests/AcceptHeaderTest.php | 103 - .../Tests/ApacheRequestTest.php | 93 - .../Tests/BinaryFileResponseTest.php | 365 -- .../http-foundation/Tests/CookieTest.php | 235 -- .../Tests/ExpressionRequestMatcherTest.php | 69 - .../http-foundation/Tests/File/FakeFile.php | 45 - .../http-foundation/Tests/File/FileTest.php | 180 - .../Tests/File/Fixtures/.unknownextension | 1 - .../Tests/File/Fixtures/directory/.empty | 0 .../Tests/File/Fixtures/other-file.example | 0 .../http-foundation/Tests/File/Fixtures/test | Bin 35 -> 0 bytes .../Tests/File/Fixtures/test.gif | Bin 35 -> 0 bytes .../Tests/File/MimeType/MimeTypeTest.php | 90 - .../Tests/File/UploadedFileTest.php | 273 -- .../http-foundation/Tests/FileBagTest.php | 175 - .../Fixtures/response-functional/common.inc | 43 - .../cookie_max_age.expected | 11 - .../response-functional/cookie_max_age.php | 10 - .../cookie_raw_urlencode.expected | 10 - .../cookie_raw_urlencode.php | 12 - .../cookie_samesite_lax.expected | 9 - .../cookie_samesite_lax.php | 8 - .../cookie_samesite_strict.expected | 9 - .../cookie_samesite_strict.php | 8 - .../cookie_urlencode.expected | 10 - .../response-functional/cookie_urlencode.php | 12 - .../invalid_cookie_name.expected | 6 - .../invalid_cookie_name.php | 11 - .../http-foundation/Tests/HeaderBagTest.php | 205 -- .../http-foundation/Tests/IpUtilsTest.php | 104 - .../Tests/JsonResponseTest.php | 266 -- .../Tests/ParameterBagTest.php | 194 - .../Tests/RedirectResponseTest.php | 97 - .../Tests/RequestMatcherTest.php | 151 - .../Tests/RequestStackTest.php | 70 - .../http-foundation/Tests/RequestTest.php | 2446 ------------- .../Tests/ResponseFunctionalTest.php | 58 - .../Tests/ResponseHeaderBagTest.php | 363 -- .../http-foundation/Tests/ResponseTest.php | 1013 ------ .../Tests/ResponseTestCase.php | 89 - .../http-foundation/Tests/ServerBagTest.php | 170 - .../Session/Attribute/AttributeBagTest.php | 186 - .../Attribute/NamespacedAttributeBagTest.php | 204 -- .../Session/Flash/AutoExpireFlashBagTest.php | 161 - .../Tests/Session/Flash/FlashBagTest.php | 157 - .../Tests/Session/SessionTest.php | 263 -- .../Handler/AbstractSessionHandlerTest.php | 61 - .../Storage/Handler/Fixtures/common.inc | 151 - .../Handler/Fixtures/empty_destroys.expected | 17 - .../Handler/Fixtures/empty_destroys.php | 8 - .../Handler/Fixtures/read_only.expected | 14 - .../Storage/Handler/Fixtures/read_only.php | 8 - .../Handler/Fixtures/regenerate.expected | 24 - .../Storage/Handler/Fixtures/regenerate.php | 10 - .../Storage/Handler/Fixtures/storage.expected | 20 - .../Storage/Handler/Fixtures/storage.php | 24 - .../Handler/Fixtures/with_cookie.expected | 15 - .../Storage/Handler/Fixtures/with_cookie.php | 8 - .../Fixtures/with_cookie_and_session.expected | 24 - .../Fixtures/with_cookie_and_session.php | 13 - .../Handler/MemcacheSessionHandlerTest.php | 135 - .../Handler/MemcachedSessionHandlerTest.php | 139 - .../Handler/MongoDbSessionHandlerTest.php | 333 -- .../Handler/NativeFileSessionHandlerTest.php | 77 - .../Handler/NativeSessionHandlerTest.php | 38 - .../Handler/NullSessionHandlerTest.php | 59 - .../Storage/Handler/PdoSessionHandlerTest.php | 411 --- .../Handler/StrictSessionHandlerTest.php | 189 - .../Handler/WriteCheckSessionHandlerTest.php | 97 - .../Tests/Session/Storage/MetadataBagTest.php | 139 - .../Storage/MockArraySessionStorageTest.php | 131 - .../Storage/MockFileSessionStorageTest.php | 127 - .../Storage/NativeSessionStorageTest.php | 294 -- .../Storage/PhpBridgeSessionStorageTest.php | 96 - .../Storage/Proxy/AbstractProxyTest.php | 113 - .../Session/Storage/Proxy/NativeProxyTest.php | 38 - .../Storage/Proxy/SessionHandlerProxyTest.php | 157 - .../Tests/StreamedResponseTest.php | 144 - .../Tests/schema/http-status-codes.rng | 31 - .../Tests/schema/iana-registry.rng | 198 -- vendor/symfony/http-foundation/UrlHelper.php | 102 + vendor/symfony/http-foundation/composer.json | 11 +- .../symfony/http-foundation/phpunit.xml.dist | 31 - vendor/symfony/mime/.gitattributes | 3 + vendor/symfony/mime/Address.php | 125 + vendor/symfony/mime/BodyRendererInterface.php | 20 + vendor/symfony/mime/CHANGELOG.md | 20 + vendor/symfony/mime/CharacterStream.php | 221 ++ vendor/symfony/mime/Crypto/SMime.php | 111 + vendor/symfony/mime/Crypto/SMimeEncrypter.php | 63 + vendor/symfony/mime/Crypto/SMimeSigner.php | 71 + .../AddMimeTypeGuesserPass.php | 46 + vendor/symfony/mime/Email.php | 599 ++++ .../mime/Encoder/AddressEncoderInterface.php | 28 + .../mime/Encoder/Base64ContentEncoder.php | 48 + vendor/symfony/mime/Encoder/Base64Encoder.php | 41 + .../mime/Encoder/Base64MimeHeaderEncoder.php | 43 + .../mime/Encoder/ContentEncoderInterface.php | 30 + .../mime/Encoder/EightBitContentEncoder.php | 35 + .../symfony/mime/Encoder/EncoderInterface.php | 26 + .../mime/Encoder/IdnAddressEncoder.php | 54 + .../Encoder/MimeHeaderEncoderInterface.php | 23 + .../symfony/mime/Encoder/QpContentEncoder.php | 64 + vendor/symfony/mime/Encoder/QpEncoder.php | 195 + .../mime/Encoder/QpMimeHeaderEncoder.php | 40 + .../symfony/mime/Encoder/Rfc2231Encoder.php | 50 + .../Exception/AddressEncoderException.php | 19 + .../mime/Exception/ExceptionInterface.php | 19 + .../Exception/InvalidArgumentException.php | 19 + .../symfony/mime/Exception/LogicException.php | 19 + .../mime/Exception/RfcComplianceException.php | 19 + .../mime/Exception/RuntimeException.php | 19 + .../mime/FileBinaryMimeTypeGuesser.php | 93 + .../symfony/mime/FileinfoMimeTypeGuesser.php | 63 + vendor/symfony/mime/Header/AbstractHeader.php | 279 ++ vendor/symfony/mime/Header/DateHeader.php | 66 + .../symfony/mime/Header/HeaderInterface.php | 65 + vendor/symfony/mime/Header/Headers.php | 282 ++ .../mime/Header/IdentificationHeader.php | 110 + vendor/symfony/mime/Header/MailboxHeader.php | 85 + .../symfony/mime/Header/MailboxListHeader.php | 136 + .../mime/Header/ParameterizedHeader.php | 174 + vendor/symfony/mime/Header/PathHeader.php | 62 + .../mime/Header/UnstructuredHeader.php | 69 + .../symfony/{polyfill-php70 => mime}/LICENSE | 2 +- vendor/symfony/mime/Message.php | 151 + vendor/symfony/mime/MessageConverter.php | 125 + .../symfony/mime/MimeTypeGuesserInterface.php | 37 + vendor/symfony/mime/MimeTypes.php | 3153 +++++++++++++++++ vendor/symfony/mime/MimeTypesInterface.php | 32 + .../mime/Part/AbstractMultipartPart.php | 99 + vendor/symfony/mime/Part/AbstractPart.php | 65 + vendor/symfony/mime/Part/DataPart.php | 161 + vendor/symfony/mime/Part/MessagePart.php | 62 + .../mime/Part/Multipart/AlternativePart.php | 25 + .../mime/Part/Multipart/DigestPart.php | 31 + .../mime/Part/Multipart/FormDataPart.php | 103 + .../symfony/mime/Part/Multipart/MixedPart.php | 25 + .../mime/Part/Multipart/RelatedPart.php | 55 + vendor/symfony/mime/Part/SMimePart.php | 116 + vendor/symfony/mime/Part/TextPart.php | 204 ++ vendor/symfony/mime/README.md | 13 + vendor/symfony/mime/RawMessage.php | 88 + .../mime/Resources/bin/update_mime_types.php | 166 + .../Test/Constraint/EmailAddressContains.php | 74 + .../Test/Constraint/EmailAttachmentCount.php | 60 + .../mime/Test/Constraint/EmailHasHeader.php | 57 + .../mime/Test/Constraint/EmailHeaderSame.php | 59 + .../Test/Constraint/EmailHtmlBodyContains.php | 58 + .../Test/Constraint/EmailTextBodyContains.php | 58 + vendor/symfony/mime/composer.json | 42 + vendor/symfony/polyfill-intl-idn/Idn.php | 283 ++ .../polyfill-intl-idn}/LICENSE | 13 +- vendor/symfony/polyfill-intl-idn/README.md | 12 + .../symfony/polyfill-intl-idn/bootstrap.php | 59 + .../symfony/polyfill-intl-idn/composer.json | 36 + vendor/symfony/polyfill-mbstring/LICENSE | 2 +- vendor/symfony/polyfill-mbstring/Mbstring.php | 51 +- .../symfony/polyfill-mbstring/bootstrap.php | 4 + .../symfony/polyfill-mbstring/composer.json | 2 +- vendor/symfony/polyfill-php70/Php70.php | 74 - vendor/symfony/polyfill-php70/README.md | 28 - .../Resources/stubs/ArithmeticError.php | 5 - .../Resources/stubs/AssertionError.php | 5 - .../Resources/stubs/DivisionByZeroError.php | 5 - .../polyfill-php70/Resources/stubs/Error.php | 5 - .../Resources/stubs/ParseError.php | 5 - ...SessionUpdateTimestampHandlerInterface.php | 23 - .../Resources/stubs/TypeError.php | 5 - vendor/symfony/polyfill-php70/bootstrap.php | 27 - vendor/symfony/polyfill-php72/LICENSE | 19 + vendor/symfony/polyfill-php72/Php72.php | 216 ++ vendor/symfony/polyfill-php72/README.md | 27 + vendor/symfony/polyfill-php72/bootstrap.php | 36 + .../composer.json | 14 +- vendor/symfony/var-dumper/.gitattributes | 3 + vendor/symfony/var-dumper/.gitignore | 3 - vendor/symfony/var-dumper/CHANGELOG.md | 42 +- .../symfony/var-dumper/Caster/AmqpCaster.php | 54 +- vendor/symfony/var-dumper/Caster/ArgsStub.php | 18 +- vendor/symfony/var-dumper/Caster/Caster.php | 47 +- .../symfony/var-dumper/Caster/ClassStub.php | 43 +- .../symfony/var-dumper/Caster/ConstStub.php | 7 +- vendor/symfony/var-dumper/Caster/CutStub.php | 5 + .../symfony/var-dumper/Caster/DOMCaster.php | 80 +- .../symfony/var-dumper/Caster/DateCaster.php | 43 +- .../var-dumper/Caster/DoctrineCaster.php | 14 +- vendor/symfony/var-dumper/Caster/DsCaster.php | 70 + .../symfony/var-dumper/Caster/DsPairStub.php | 28 + vendor/symfony/var-dumper/Caster/EnumStub.php | 2 +- .../var-dumper/Caster/ExceptionCaster.php | 88 +- .../symfony/var-dumper/Caster/FrameStub.php | 2 +- .../symfony/var-dumper/Caster/GmpCaster.php | 32 + .../var-dumper/Caster/ImagineCaster.php | 37 + vendor/symfony/var-dumper/Caster/ImgStub.php | 26 + .../symfony/var-dumper/Caster/IntlCaster.php | 172 + vendor/symfony/var-dumper/Caster/LinkStub.php | 8 +- .../var-dumper/Caster/MemcachedCaster.php | 81 + .../symfony/var-dumper/Caster/MongoCaster.php | 38 - .../symfony/var-dumper/Caster/PdoCaster.php | 30 +- .../symfony/var-dumper/Caster/PgSqlCaster.php | 22 +- .../var-dumper/Caster/ProxyManagerCaster.php | 33 + .../symfony/var-dumper/Caster/RedisCaster.php | 125 +- .../var-dumper/Caster/ReflectionCaster.php | 177 +- .../var-dumper/Caster/ResourceCaster.php | 28 + .../symfony/var-dumper/Caster/SplCaster.php | 61 +- .../symfony/var-dumper/Caster/StubCaster.php | 8 +- .../var-dumper/Caster/SymfonyCaster.php | 32 +- .../symfony/var-dumper/Caster/TraceStub.php | 2 +- .../symfony/var-dumper/Caster/UuidCaster.php | 30 + .../var-dumper/Caster/XmlReaderCaster.php | 18 +- .../var-dumper/Caster/XmlResourceCaster.php | 6 +- .../var-dumper/Cloner/AbstractCloner.php | 286 +- vendor/symfony/var-dumper/Cloner/Cursor.php | 2 +- vendor/symfony/var-dumper/Cloner/Data.php | 110 +- .../var-dumper/Cloner/DumperInterface.php | 28 +- vendor/symfony/var-dumper/Cloner/Stub.php | 32 +- .../symfony/var-dumper/Cloner/VarCloner.php | 99 +- .../Command/Descriptor/CliDescriptor.php | 88 + .../Descriptor/DumpDescriptorInterface.php | 23 + .../Command/Descriptor/HtmlDescriptor.php | 119 + .../var-dumper/Command/ServerDumpCommand.php | 99 + .../var-dumper/Dumper/AbstractDumper.php | 21 +- .../symfony/var-dumper/Dumper/CliDumper.php | 135 +- .../ContextProvider/CliContextProvider.php | 32 + .../ContextProviderInterface.php | 25 + .../RequestContextProvider.php | 51 + .../ContextProvider/SourceContextProvider.php | 126 + .../Dumper/ContextualizedDumper.php | 43 + .../symfony/var-dumper/Dumper/HtmlDumper.php | 202 +- .../var-dumper/Dumper/ServerDumper.php | 53 + .../Exception/ThrowingCasterException.php | 4 +- vendor/symfony/var-dumper/LICENSE | 2 +- vendor/symfony/var-dumper/README.md | 4 +- .../var-dumper/Resources/bin/var-dump-server | 63 + .../Resources/css/htmlDescriptor.css | 130 + .../var-dumper/Resources/functions/dump.php | 17 +- .../var-dumper/Resources/js/htmlDescriptor.js | 10 + .../symfony/var-dumper/Server/Connection.php | 95 + .../symfony/var-dumper/Server/DumpServer.php | 107 + .../var-dumper/Test/VarDumperTestTrait.php | 61 +- .../var-dumper/Tests/Caster/CasterTest.php | 181 - .../Tests/Caster/DateCasterTest.php | 426 --- .../Tests/Caster/ExceptionCasterTest.php | 230 -- .../var-dumper/Tests/Caster/PdoCasterTest.php | 64 - .../Tests/Caster/RedisCasterTest.php | 84 - .../Tests/Caster/ReflectionCasterTest.php | 270 -- .../var-dumper/Tests/Caster/SplCasterTest.php | 213 -- .../Tests/Caster/StubCasterTest.php | 192 - .../Tests/Caster/XmlReaderCasterTest.php | 248 -- .../var-dumper/Tests/Cloner/DataTest.php | 115 - .../var-dumper/Tests/Cloner/VarClonerTest.php | 437 --- .../var-dumper/Tests/Dumper/CliDumperTest.php | 591 --- .../var-dumper/Tests/Dumper/FunctionsTest.php | 57 - .../Tests/Dumper/HtmlDumperTest.php | 168 - .../Tests/Fixtures/FooInterface.php | 11 - .../Tests/Fixtures/GeneratorDemo.php | 21 - .../Tests/Fixtures/NotLoadableClass.php | 7 - .../var-dumper/Tests/Fixtures/Twig.php | 38 - .../var-dumper/Tests/Fixtures/dumb-var.php | 40 - .../var-dumper/Tests/Fixtures/xml_reader.xml | 10 - .../Tests/Test/VarDumperTestTraitTest.php | 41 - vendor/symfony/var-dumper/VarDumper.php | 16 +- vendor/symfony/var-dumper/composer.json | 19 +- vendor/symfony/var-dumper/phpunit.xml.dist | 33 - vendor/tightenco/collect/.gitignore | 1 - vendor/tightenco/collect/collect-logo.png | Bin 18320 -> 0 bytes vendor/tightenco/collect/composer.json | 15 +- vendor/tightenco/collect/composer.lock | 1711 --------- vendor/tightenco/collect/phpunit.xml | 20 - vendor/tightenco/collect/readme.md | 28 - .../Collect/Contracts/Support/Arrayable.php | 0 .../Collect/Contracts/Support/Jsonable.php | 0 .../collect/src/Collect/Support/Arr.php | 58 +- .../src/Collect/Support/Collection.php | 515 ++- .../src/Collect/Support/Debug/Dumper.php | 26 - .../src/Collect/Support/Debug/HtmlDumper.php | 29 - .../src/Collect/Support/Traits/Macroable.php | 34 +- .../collect/src/Collect/Support/alias.php | 21 +- .../collect/src/Collect/Support/helpers.php | 184 +- .../tests/Support/SupportCollectionTest.php | 2627 -------------- vendor/tightenco/collect/tests/bootstrap.php | 5 - 593 files changed, 24068 insertions(+), 31184 deletions(-) create mode 120000 vendor/bin/var-dump-server create mode 100644 vendor/botman/botman/src/Messages/Attachments/Contact.php create mode 100644 vendor/botman/driver-facebook/src/Events/MessagingAccountLinking.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/AbstractAirlineTemplate.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AbstractAirlineFlightInfo.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlineAirport.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlineBoardingPass.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlineExtendedFlightInfo.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlineFlightInfo.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlineFlightSchedule.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerInfo.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerSegmentInfo.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/AirlineBoardingPassTemplate.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/AirlineCheckInTemplate.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/AirlineItineraryTemplate.php create mode 100644 vendor/botman/driver-facebook/src/Extensions/AirlineUpdateTemplate.php create mode 100644 vendor/botman/driver-facebook/src/Interfaces/Airline.php delete mode 100755 vendor/paragonie/random_compat/build-phar.sh delete mode 100644 vendor/paragonie/random_compat/composer.json delete mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey delete mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc delete mode 100644 vendor/paragonie/random_compat/lib/random.php delete mode 100644 vendor/paragonie/random_compat/other/build_phar.php delete mode 100644 vendor/paragonie/random_compat/psalm-autoload.php delete mode 100644 vendor/paragonie/random_compat/psalm.xml delete mode 100644 vendor/react/dns/src/Config/FilesystemFactory.php delete mode 100644 vendor/react/dns/src/Model/HeaderBag.php delete mode 100644 vendor/react/dns/src/Query/CachedExecutor.php create mode 100644 vendor/react/dns/src/Query/CachingExecutor.php create mode 100644 vendor/react/dns/src/Query/CoopExecutor.php delete mode 100644 vendor/react/dns/src/Query/Executor.php delete mode 100644 vendor/react/dns/src/Query/RecordBag.php delete mode 100644 vendor/react/dns/src/Query/RecordCache.php create mode 100644 vendor/react/dns/src/Query/SelectiveTransportExecutor.php create mode 100644 vendor/react/dns/src/Query/TcpTransportExecutor.php create mode 100644 vendor/react/dns/src/Resolver/ResolverInterface.php delete mode 100644 vendor/react/dns/tests/Config/FilesystemFactoryTest.php delete mode 100644 vendor/react/dns/tests/Query/CachedExecutorTest.php create mode 100644 vendor/react/dns/tests/Query/CachingExecutorTest.php create mode 100644 vendor/react/dns/tests/Query/CoopExecutorTest.php delete mode 100644 vendor/react/dns/tests/Query/ExecutorTest.php delete mode 100644 vendor/react/dns/tests/Query/RecordBagTest.php delete mode 100644 vendor/react/dns/tests/Query/RecordCacheTest.php create mode 100644 vendor/react/dns/tests/Query/SelectiveTransportExecutorTest.php create mode 100644 vendor/react/dns/tests/Query/TcpTransportExecutorTest.php delete mode 100644 vendor/react/event-loop/.gitignore delete mode 100644 vendor/react/event-loop/.travis.yml delete mode 100644 vendor/react/event-loop/examples/01-timers.php delete mode 100644 vendor/react/event-loop/examples/02-periodic.php delete mode 100644 vendor/react/event-loop/examples/03-ticks.php delete mode 100644 vendor/react/event-loop/examples/04-signals.php delete mode 100644 vendor/react/event-loop/examples/11-consume-stdin.php delete mode 100644 vendor/react/event-loop/examples/12-generate-yes.php delete mode 100644 vendor/react/event-loop/examples/13-http-client-blocking.php delete mode 100644 vendor/react/event-loop/examples/14-http-client-async.php delete mode 100644 vendor/react/event-loop/examples/21-http-server.php delete mode 100644 vendor/react/event-loop/examples/91-benchmark-ticks.php delete mode 100644 vendor/react/event-loop/examples/92-benchmark-timers.php delete mode 100644 vendor/react/event-loop/examples/93-benchmark-ticks-delay.php delete mode 100644 vendor/react/event-loop/examples/94-benchmark-timers-delay.php delete mode 100644 vendor/react/event-loop/examples/95-benchmark-memory.php delete mode 100644 vendor/react/event-loop/phpunit.xml.dist create mode 100644 vendor/react/event-loop/src/ExtUvLoop.php delete mode 100644 vendor/react/event-loop/tests/AbstractLoopTest.php delete mode 100644 vendor/react/event-loop/tests/CallableStub.php delete mode 100644 vendor/react/event-loop/tests/ExtEvLoopTest.php delete mode 100644 vendor/react/event-loop/tests/ExtEventLoopTest.php delete mode 100644 vendor/react/event-loop/tests/ExtLibevLoopTest.php delete mode 100644 vendor/react/event-loop/tests/ExtLibeventLoopTest.php delete mode 100644 vendor/react/event-loop/tests/SignalsHandlerTest.php delete mode 100644 vendor/react/event-loop/tests/StreamSelectLoopTest.php delete mode 100644 vendor/react/event-loop/tests/TestCase.php delete mode 100644 vendor/react/event-loop/tests/Timer/AbstractTimerTest.php delete mode 100644 vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php delete mode 100644 vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php delete mode 100644 vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php delete mode 100644 vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php delete mode 100644 vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php delete mode 100644 vendor/react/event-loop/tests/Timer/TimersTest.php delete mode 100644 vendor/react/event-loop/tests/bootstrap.php delete mode 100755 vendor/react/event-loop/travis-init.sh create mode 100644 vendor/react/promise-timer/src/functions_include.php delete mode 100644 vendor/react/socket/.gitignore delete mode 100644 vendor/react/socket/.travis.yml delete mode 100644 vendor/react/socket/examples/01-echo-server.php delete mode 100644 vendor/react/socket/examples/02-chat-server.php delete mode 100644 vendor/react/socket/examples/03-http-server.php delete mode 100644 vendor/react/socket/examples/11-http-client.php delete mode 100644 vendor/react/socket/examples/12-https-client.php delete mode 100644 vendor/react/socket/examples/21-netcat-client.php delete mode 100644 vendor/react/socket/examples/22-http-client.php delete mode 100644 vendor/react/socket/examples/91-benchmark-server.php delete mode 100644 vendor/react/socket/examples/99-generate-self-signed.php delete mode 100644 vendor/react/socket/examples/localhost.pem delete mode 100644 vendor/react/socket/examples/localhost_swordfish.pem delete mode 100644 vendor/react/socket/phpunit.xml.dist create mode 100644 vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php create mode 100644 vendor/react/socket/src/HappyEyeBallsConnector.php delete mode 100644 vendor/react/socket/tests/ConnectionTest.php delete mode 100644 vendor/react/socket/tests/ConnectorTest.php delete mode 100644 vendor/react/socket/tests/DnsConnectorTest.php delete mode 100644 vendor/react/socket/tests/FixedUriConnectorTest.php delete mode 100644 vendor/react/socket/tests/FunctionalConnectorTest.php delete mode 100644 vendor/react/socket/tests/FunctionalSecureServerTest.php delete mode 100644 vendor/react/socket/tests/FunctionalTcpServerTest.php delete mode 100644 vendor/react/socket/tests/IntegrationTest.php delete mode 100644 vendor/react/socket/tests/LimitingServerTest.php delete mode 100644 vendor/react/socket/tests/SecureConnectorTest.php delete mode 100644 vendor/react/socket/tests/SecureIntegrationTest.php delete mode 100644 vendor/react/socket/tests/SecureServerTest.php delete mode 100644 vendor/react/socket/tests/ServerTest.php delete mode 100644 vendor/react/socket/tests/Stub/CallableStub.php delete mode 100644 vendor/react/socket/tests/Stub/ConnectionStub.php delete mode 100644 vendor/react/socket/tests/Stub/ServerStub.php delete mode 100644 vendor/react/socket/tests/TcpConnectorTest.php delete mode 100644 vendor/react/socket/tests/TcpServerTest.php delete mode 100644 vendor/react/socket/tests/TestCase.php delete mode 100644 vendor/react/socket/tests/TimeoutConnectorTest.php delete mode 100644 vendor/react/socket/tests/UnixConnectorTest.php delete mode 100644 vendor/react/socket/tests/UnixServerTest.php create mode 100644 vendor/symfony/http-foundation/.gitattributes delete mode 100644 vendor/symfony/http-foundation/.gitignore create mode 100644 vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/NoFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/PartialFileException.php create mode 100644 vendor/symfony/http-foundation/HeaderUtils.php create mode 100644 vendor/symfony/http-foundation/Session/SessionUtils.php delete mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php delete mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php delete mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php delete mode 100644 vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php create mode 100644 vendor/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php delete mode 100644 vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ApacheRequestTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/CookieTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/File/FakeFile.php delete mode 100644 vendor/symfony/http-foundation/Tests/File/FileTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension delete mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty delete mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example delete mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/test delete mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif delete mode 100644 vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/FileBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php delete mode 100644 vendor/symfony/http-foundation/Tests/HeaderBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/IpUtilsTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/JsonResponseTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ParameterBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/RedirectResponseTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/RequestMatcherTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/RequestStackTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/RequestTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ResponseTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/ResponseTestCase.php delete mode 100644 vendor/symfony/http-foundation/Tests/ServerBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/SessionTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/StreamedResponseTest.php delete mode 100644 vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng delete mode 100644 vendor/symfony/http-foundation/Tests/schema/iana-registry.rng create mode 100644 vendor/symfony/http-foundation/UrlHelper.php delete mode 100644 vendor/symfony/http-foundation/phpunit.xml.dist create mode 100644 vendor/symfony/mime/.gitattributes create mode 100644 vendor/symfony/mime/Address.php create mode 100644 vendor/symfony/mime/BodyRendererInterface.php create mode 100644 vendor/symfony/mime/CHANGELOG.md create mode 100644 vendor/symfony/mime/CharacterStream.php create mode 100644 vendor/symfony/mime/Crypto/SMime.php create mode 100644 vendor/symfony/mime/Crypto/SMimeEncrypter.php create mode 100644 vendor/symfony/mime/Crypto/SMimeSigner.php create mode 100644 vendor/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php create mode 100644 vendor/symfony/mime/Email.php create mode 100644 vendor/symfony/mime/Encoder/AddressEncoderInterface.php create mode 100644 vendor/symfony/mime/Encoder/Base64ContentEncoder.php create mode 100644 vendor/symfony/mime/Encoder/Base64Encoder.php create mode 100644 vendor/symfony/mime/Encoder/Base64MimeHeaderEncoder.php create mode 100644 vendor/symfony/mime/Encoder/ContentEncoderInterface.php create mode 100644 vendor/symfony/mime/Encoder/EightBitContentEncoder.php create mode 100644 vendor/symfony/mime/Encoder/EncoderInterface.php create mode 100644 vendor/symfony/mime/Encoder/IdnAddressEncoder.php create mode 100644 vendor/symfony/mime/Encoder/MimeHeaderEncoderInterface.php create mode 100644 vendor/symfony/mime/Encoder/QpContentEncoder.php create mode 100644 vendor/symfony/mime/Encoder/QpEncoder.php create mode 100644 vendor/symfony/mime/Encoder/QpMimeHeaderEncoder.php create mode 100644 vendor/symfony/mime/Encoder/Rfc2231Encoder.php create mode 100644 vendor/symfony/mime/Exception/AddressEncoderException.php create mode 100644 vendor/symfony/mime/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/mime/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/mime/Exception/LogicException.php create mode 100644 vendor/symfony/mime/Exception/RfcComplianceException.php create mode 100644 vendor/symfony/mime/Exception/RuntimeException.php create mode 100644 vendor/symfony/mime/FileBinaryMimeTypeGuesser.php create mode 100644 vendor/symfony/mime/FileinfoMimeTypeGuesser.php create mode 100644 vendor/symfony/mime/Header/AbstractHeader.php create mode 100644 vendor/symfony/mime/Header/DateHeader.php create mode 100644 vendor/symfony/mime/Header/HeaderInterface.php create mode 100644 vendor/symfony/mime/Header/Headers.php create mode 100644 vendor/symfony/mime/Header/IdentificationHeader.php create mode 100644 vendor/symfony/mime/Header/MailboxHeader.php create mode 100644 vendor/symfony/mime/Header/MailboxListHeader.php create mode 100644 vendor/symfony/mime/Header/ParameterizedHeader.php create mode 100644 vendor/symfony/mime/Header/PathHeader.php create mode 100644 vendor/symfony/mime/Header/UnstructuredHeader.php rename vendor/symfony/{polyfill-php70 => mime}/LICENSE (96%) create mode 100644 vendor/symfony/mime/Message.php create mode 100644 vendor/symfony/mime/MessageConverter.php create mode 100644 vendor/symfony/mime/MimeTypeGuesserInterface.php create mode 100644 vendor/symfony/mime/MimeTypes.php create mode 100644 vendor/symfony/mime/MimeTypesInterface.php create mode 100644 vendor/symfony/mime/Part/AbstractMultipartPart.php create mode 100644 vendor/symfony/mime/Part/AbstractPart.php create mode 100644 vendor/symfony/mime/Part/DataPart.php create mode 100644 vendor/symfony/mime/Part/MessagePart.php create mode 100644 vendor/symfony/mime/Part/Multipart/AlternativePart.php create mode 100644 vendor/symfony/mime/Part/Multipart/DigestPart.php create mode 100644 vendor/symfony/mime/Part/Multipart/FormDataPart.php create mode 100644 vendor/symfony/mime/Part/Multipart/MixedPart.php create mode 100644 vendor/symfony/mime/Part/Multipart/RelatedPart.php create mode 100644 vendor/symfony/mime/Part/SMimePart.php create mode 100644 vendor/symfony/mime/Part/TextPart.php create mode 100644 vendor/symfony/mime/README.md create mode 100644 vendor/symfony/mime/RawMessage.php create mode 100644 vendor/symfony/mime/Resources/bin/update_mime_types.php create mode 100644 vendor/symfony/mime/Test/Constraint/EmailAddressContains.php create mode 100644 vendor/symfony/mime/Test/Constraint/EmailAttachmentCount.php create mode 100644 vendor/symfony/mime/Test/Constraint/EmailHasHeader.php create mode 100644 vendor/symfony/mime/Test/Constraint/EmailHeaderSame.php create mode 100644 vendor/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php create mode 100644 vendor/symfony/mime/Test/Constraint/EmailTextBodyContains.php create mode 100644 vendor/symfony/mime/composer.json create mode 100644 vendor/symfony/polyfill-intl-idn/Idn.php rename vendor/{paragonie/random_compat => symfony/polyfill-intl-idn}/LICENSE (85%) create mode 100644 vendor/symfony/polyfill-intl-idn/README.md create mode 100644 vendor/symfony/polyfill-intl-idn/bootstrap.php create mode 100644 vendor/symfony/polyfill-intl-idn/composer.json delete mode 100644 vendor/symfony/polyfill-php70/Php70.php delete mode 100644 vendor/symfony/polyfill-php70/README.md delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/Error.php delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php delete mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php delete mode 100644 vendor/symfony/polyfill-php70/bootstrap.php create mode 100644 vendor/symfony/polyfill-php72/LICENSE create mode 100644 vendor/symfony/polyfill-php72/Php72.php create mode 100644 vendor/symfony/polyfill-php72/README.md create mode 100644 vendor/symfony/polyfill-php72/bootstrap.php rename vendor/symfony/{polyfill-php70 => polyfill-php72}/composer.json (61%) create mode 100644 vendor/symfony/var-dumper/.gitattributes delete mode 100644 vendor/symfony/var-dumper/.gitignore create mode 100644 vendor/symfony/var-dumper/Caster/DsCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/DsPairStub.php create mode 100644 vendor/symfony/var-dumper/Caster/GmpCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ImagineCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ImgStub.php create mode 100644 vendor/symfony/var-dumper/Caster/IntlCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/MemcachedCaster.php delete mode 100644 vendor/symfony/var-dumper/Caster/MongoCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/UuidCaster.php create mode 100644 vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php create mode 100644 vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php create mode 100644 vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php create mode 100644 vendor/symfony/var-dumper/Command/ServerDumpCommand.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/ServerDumper.php create mode 100755 vendor/symfony/var-dumper/Resources/bin/var-dump-server create mode 100644 vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css create mode 100644 vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js create mode 100644 vendor/symfony/var-dumper/Server/Connection.php create mode 100644 vendor/symfony/var-dumper/Server/DumpServer.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/CasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Cloner/DataTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php delete mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/FooInterface.php delete mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php delete mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/NotLoadableClass.php delete mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/Twig.php delete mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php delete mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml delete mode 100644 vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php delete mode 100644 vendor/symfony/var-dumper/phpunit.xml.dist delete mode 100644 vendor/tightenco/collect/.gitignore delete mode 100644 vendor/tightenco/collect/collect-logo.png delete mode 100644 vendor/tightenco/collect/composer.lock delete mode 100644 vendor/tightenco/collect/phpunit.xml delete mode 100644 vendor/tightenco/collect/readme.md mode change 100644 => 100755 vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php mode change 100644 => 100755 vendor/tightenco/collect/src/Collect/Contracts/Support/Jsonable.php mode change 100644 => 100755 vendor/tightenco/collect/src/Collect/Support/Arr.php delete mode 100644 vendor/tightenco/collect/src/Collect/Support/Debug/Dumper.php delete mode 100644 vendor/tightenco/collect/src/Collect/Support/Debug/HtmlDumper.php mode change 100644 => 100755 vendor/tightenco/collect/src/Collect/Support/helpers.php delete mode 100644 vendor/tightenco/collect/tests/Support/SupportCollectionTest.php delete mode 100644 vendor/tightenco/collect/tests/bootstrap.php diff --git a/composer.lock b/composer.lock index 293eb6c..14c40d6 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "botman/botman", - "version": "2.4.1", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/botman/botman.git", - "reference": "b4d07814c5848fce1e841bba836c7d1a3ee51d29" + "reference": "c49cacb56f0b30178986f77e4fbc572b56c2e7bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/botman/botman/zipball/b4d07814c5848fce1e841bba836c7d1a3ee51d29", - "reference": "b4d07814c5848fce1e841bba836c7d1a3ee51d29", + "url": "https://api.github.com/repos/botman/botman/zipball/c49cacb56f0b30178986f77e4fbc572b56c2e7bc", + "reference": "c49cacb56f0b30178986f77e4fbc572b56c2e7bc", "shasum": "" }, "require": { @@ -25,7 +25,7 @@ "opis/closure": "^2.3 || ^3.0", "php": ">=7.0", "psr/container": "^1.0", - "react/socket": "~0.4", + "react/socket": "~1.0", "spatie/macroable": "^1.0", "symfony/http-foundation": "^2.8 || ^3.0 || ^4.0", "tightenco/collect": "~5.0" @@ -81,20 +81,20 @@ "bot", "chatbot" ], - "time": "2018-08-09T13:15:19+00:00" + "time": "2019-05-14T21:18:37+00:00" }, { "name": "botman/driver-facebook", - "version": "1.9.4", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/botman/driver-facebook.git", - "reference": "8071f777388de7cde8122a340acdab01694d517b" + "reference": "a02096c5b9dafddc3353e8c5eff060fcb004985f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/botman/driver-facebook/zipball/8071f777388de7cde8122a340acdab01694d517b", - "reference": "8071f777388de7cde8122a340acdab01694d517b", + "url": "https://api.github.com/repos/botman/driver-facebook/zipball/a02096c5b9dafddc3353e8c5eff060fcb004985f", + "reference": "a02096c5b9dafddc3353e8c5eff060fcb004985f", "shasum": "" }, "require": { @@ -143,7 +143,7 @@ "facebook", "facebook messenger" ], - "time": "2018-10-04T06:45:12+00:00" + "time": "2019-03-19T06:36:25+00:00" }, { "name": "evenement/evenement", @@ -234,16 +234,16 @@ }, { "name": "opis/closure", - "version": "3.1.5", + "version": "3.5.1", "source": { "type": "git", "url": "https://github.com/opis/closure.git", - "reference": "41f5da65d75cf473e5ee582df8fc7f2c733ce9d6" + "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/41f5da65d75cf473e5ee582df8fc7f2c733ce9d6", - "reference": "41f5da65d75cf473e5ee582df8fc7f2c733ce9d6", + "url": "https://api.github.com/repos/opis/closure/zipball/93ebc5712cdad8d5f489b500c59d122df2e53969", + "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969", "shasum": "" }, "require": { @@ -251,12 +251,12 @@ }, "require-dev": { "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0|^5.0|^6.0|^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "3.5.x-dev" } }, "autoload": { @@ -291,52 +291,7 @@ "serialization", "serialize" ], - "time": "2019-01-14T14:45:33+00:00" - }, - { - "name": "paragonie/random_compat", - "version": "v9.99.99", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "shasum": "" - }, - "require": { - "php": "^7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "time": "2018-07-02T15:55:56+00:00" + "time": "2019-11-29T22:36:02+00:00" }, { "name": "psr/container", @@ -389,16 +344,16 @@ }, { "name": "react/cache", - "version": "v0.5.0", + "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/reactphp/cache.git", - "reference": "7d7da7fb7574d471904ba357b39bbf110ccdbf66" + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/7d7da7fb7574d471904ba357b39bbf110ccdbf66", - "reference": "7d7da7fb7574d471904ba357b39bbf110ccdbf66", + "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", "shasum": "" }, "require": { @@ -425,33 +380,32 @@ "promise", "reactphp" ], - "time": "2018-06-25T12:52:40+00:00" + "time": "2019-07-11T13:45:28+00:00" }, { "name": "react/dns", - "version": "v0.4.16", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "0a0bedfec72b38406413c6ea01e1c015bd0bf72b" + "reference": "a214d90c2884dac18d0cac6176202f247b66d762" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/0a0bedfec72b38406413c6ea01e1c015bd0bf72b", - "reference": "0a0bedfec72b38406413c6ea01e1c015bd0bf72b", + "url": "https://api.github.com/repos/reactphp/dns/zipball/a214d90c2884dac18d0cac6176202f247b66d762", + "reference": "a214d90c2884dac18d0cac6176202f247b66d762", "shasum": "" }, "require": { "php": ">=5.3.0", - "react/cache": "^0.5 || ^0.4 || ^0.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.1 || ^1.2.1", - "react/promise-timer": "^1.2", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5", + "react/promise": "^2.7 || ^1.2.1", + "react/promise-timer": "^1.2" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -470,31 +424,32 @@ "dns-resolver", "reactphp" ], - "time": "2018-11-11T11:21:13+00:00" + "time": "2019-08-15T09:06:31+00:00" }, { "name": "react/event-loop", - "version": "v1.0.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "0266aff7aa7b0613b1f38a723e14a0ebc55cfca3" + "reference": "6d24de090cd59cfc830263cfba965be77b563c13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/0266aff7aa7b0613b1f38a723e14a0ebc55cfca3", - "reference": "0266aff7aa7b0613b1f38a723e14a0ebc55cfca3", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13", + "reference": "6d24de090cd59cfc830263cfba965be77b563c13", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "suggest": { "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" }, "type": "library", "autoload": { @@ -511,7 +466,7 @@ "asynchronous", "event-loop" ], - "time": "2018-07-11T14:37:46+00:00" + "time": "2020-01-01T18:39:52+00:00" }, { "name": "react/promise", @@ -561,16 +516,16 @@ }, { "name": "react/promise-timer", - "version": "v1.5.0", + "version": "v1.5.1", "source": { "type": "git", "url": "https://github.com/reactphp/promise-timer.git", - "reference": "a11206938ca2394dc7bb368f5da25cd4533fa603" + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/a11206938ca2394dc7bb368f5da25cd4533fa603", - "reference": "a11206938ca2394dc7bb368f5da25cd4533fa603", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", "shasum": "" }, "require": { @@ -587,7 +542,7 @@ "React\\Promise\\Timer\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -610,34 +565,35 @@ "timeout", "timer" ], - "time": "2018-06-13T16:45:37+00:00" + "time": "2019-03-27T18:10:32+00:00" }, { "name": "react/socket", - "version": "v0.8.12", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c" + "reference": "97522e24987365e1ed873f0f4884900747a668e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/7f7e6c56ccda7418a1a264892a625f38a5bdee0c", - "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "url": "https://api.github.com/repos/reactphp/socket/zipball/97522e24987365e1ed873f0f4884900747a668e0", + "reference": "97522e24987365e1ed873f0f4884900747a668e0", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "php": ">=5.3.0", - "react/dns": "^0.4.13", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/dns": "^1.1", + "react/event-loop": "^1.0 || ^0.5", "react/promise": "^2.6.0 || ^1.2.1", "react/promise-timer": "^1.4.0", - "react/stream": "^1.0 || ^0.7.1" + "react/stream": "^1.1" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.5 || ^6.4 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" }, "type": "library", "autoload": { @@ -657,7 +613,7 @@ "reactphp", "stream" ], - "time": "2018-06-11T14:33:43+00:00" + "time": "2020-03-12T12:15:14+00:00" }, { "name": "react/stream", @@ -753,30 +709,31 @@ }, { "name": "symfony/http-foundation", - "version": "v3.4.21", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "2b97319e68816d2120eee7f13f4b76da12e04d03" + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/2b97319e68816d2120eee7f13f4b76da12e04d03", - "reference": "2b97319e68816d2120eee7f13f4b76da12e04d03", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7e41b4fcad4619535f45f8bfa7744c4f384e1648", + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php70": "~1.6" + "php": "^7.1.3", + "symfony/mime": "^4.3|^5.0", + "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" + "predis/predis": "~1.0", + "symfony/expression-language": "^3.4|^4.0|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -803,20 +760,144 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-01-05T08:05:37+00:00" + "time": "2020-02-13T19:40:01+00:00" + }, + { + "name": "symfony/mime", + "version": "v5.0.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10", + "symfony/dependency-injection": "^4.4|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A library to manipulate MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "time": "2020-02-04T09:41:09+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6842f1a39cf7d580655688069a03dd7cd83d244a", + "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.14-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2020-01-17T12:01:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", "shasum": "" }, "require": { @@ -828,7 +909,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -862,41 +943,37 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { - "name": "symfony/polyfill-php70", - "version": "v1.10.0", + "name": "symfony/polyfill-php72", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" + "Symfony\\Polyfill\\Php72\\": "" }, "files": [ "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -913,7 +990,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -921,42 +998,49 @@ "portable", "shim" ], - "time": "2018-09-21T06:26:08+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/var-dumper", - "version": "v3.4.21", + "version": "v4.4.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2" + "reference": "2572839911702b0405479410ea7a1334bfab0b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a5f39641bb62e8b74e343467b145331273f615a2", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2572839911702b0405479410ea7a1334bfab0b96", + "reference": "2572839911702b0405479410ea7a1334bfab0b96", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.0" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/console": "<3.4" }, "require-dev": { "ext-iconv": "*", - "twig/twig": "~1.34|~2.4" + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^4.4|^5.0", + "twig/twig": "^1.34|^2.4|^3.0" }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", "ext-intl": "To show region name in time zone dump", - "ext-symfony_debug": "" + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" }, + "bin": [ + "Resources/bin/var-dump-server" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -990,29 +1074,30 @@ "debug", "dump" ], - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-02-24T13:10:00+00:00" }, { "name": "tightenco/collect", - "version": "v5.5.33", + "version": "v5.8.35", "source": { "type": "git", "url": "https://github.com/tightenco/collect.git", - "reference": "a2a3ca1c56aa18948b4e08e90f38fcdee859cc4c" + "reference": "c93a7039e6207ad533a09109838fe80933fcc72c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tightenco/collect/zipball/a2a3ca1c56aa18948b4e08e90f38fcdee859cc4c", - "reference": "a2a3ca1c56aa18948b4e08e90f38fcdee859cc4c", + "url": "https://api.github.com/repos/tightenco/collect/zipball/c93a7039e6207ad533a09109838fe80933fcc72c", + "reference": "c93a7039e6207ad533a09109838fe80933fcc72c", "shasum": "" }, "require": { - "php": ">=7.0", - "symfony/var-dumper": "~3.3" + "php": "^7.1.3", + "symfony/var-dumper": ">=3.4 <5" }, "require-dev": { - "mockery/mockery": "~1.0", - "phpunit/phpunit": "~6.0" + "mockery/mockery": "^1.0", + "nesbot/carbon": "^1.26.3", + "phpunit/phpunit": "^7.0" }, "type": "library", "autoload": { @@ -1039,7 +1124,7 @@ "collection", "laravel" ], - "time": "2018-02-09T02:05:17+00:00" + "time": "2019-09-17T18:57:01+00:00" } ], "packages-dev": [], diff --git a/info.xml b/info.xml index 928408c..2778981 100644 --- a/info.xml +++ b/info.xml @@ -14,8 +14,8 @@ https://thirdsectordesign.org/chatbot http://www.gnu.org/licenses/agpl-3.0.html - 2019-01-29 - 1.0.0-beta.4 + 2019-03-18 + 1.0.0-beta.5 beta 5.0 diff --git a/vendor/bin/var-dump-server b/vendor/bin/var-dump-server new file mode 120000 index 0000000..6bd4e93 --- /dev/null +++ b/vendor/bin/var-dump-server @@ -0,0 +1 @@ +../symfony/var-dumper/Resources/bin/var-dump-server \ No newline at end of file diff --git a/vendor/botman/botman/README.md b/vendor/botman/botman/README.md index 47eb2e3..e4024a8 100644 --- a/vendor/botman/botman/README.md +++ b/vendor/botman/botman/README.md @@ -10,6 +10,10 @@ [![Slack](https://rauchg-slackin-jtdkltstsj.now.sh/badge.svg)](https://rauchg-slackin-jtdkltstsj.now.sh) [![Monthly Downloads](https://img.shields.io/packagist/dm/botman/botman.svg?style=flat-square)](https://packagist.org/packages/botman/botman) +[![https://phppackagedevelopment.com](https://display-demo.schlein.net/images/phppd.jpg)](https://phppackagedevelopment.com) + +If you want to learn how to create reusable PHP packages yourself, take a look at my upcoming [PHP Package Development](https://phppackagedevelopment.com) video course. + ## About BotMan BotMan is a framework agnostic PHP library that is designed to simplify the task of developing innovative bots for multiple messaging platforms, including [Slack](https://slack.com), [Telegram](https://telegram.org), [Microsoft Bot Framework](https://dev.botframework.com), [Nexmo](https://www.nexmo.com), [HipChat](https://www.hipchat.com), [Facebook Messenger](https://www.messenger.com) and [WeChat](https://web.wechat.com). @@ -20,6 +24,8 @@ $botman->hears('I want cross-platform bots with PHP!', function (BotMan $bot) { }); ``` +> If you want to learn how to create reusable PHP packages yourself, take a look at my upcoming [PHP Package Development](https://phppackagedevelopment.com) video course. + ## Documentation You can find the BotMan documentation at [https://botman.io](https://botman.io). @@ -35,6 +41,15 @@ You can find the BotMan documentation at [https://botman.io](https://botman.io). Please see [CONTRIBUTING](CONTRIBUTING.md) for details. +[![0](https://sourcerer.io/fame/sergey48k/botman/botman/images/0)](https://sourcerer.io/fame/sergey48k/botman/botman/links/0) +[![1](https://sourcerer.io/fame/sergey48k/botman/botman/images/1)](https://sourcerer.io/fame/sergey48k/botman/botman/links/1) +[![2](https://sourcerer.io/fame/sergey48k/botman/botman/images/2)](https://sourcerer.io/fame/sergey48k/botman/botman/links/2) +[![3](https://sourcerer.io/fame/sergey48k/botman/botman/images/3)](https://sourcerer.io/fame/sergey48k/botman/botman/links/3) +[![4](https://sourcerer.io/fame/sergey48k/botman/botman/images/4)](https://sourcerer.io/fame/sergey48k/botman/botman/links/4) +[![5](https://sourcerer.io/fame/sergey48k/botman/botman/images/5)](https://sourcerer.io/fame/sergey48k/botman/botman/links/5) +[![6](https://sourcerer.io/fame/sergey48k/botman/botman/images/6)](https://sourcerer.io/fame/sergey48k/botman/botman/links/6) +[![7](https://sourcerer.io/fame/sergey48k/botman/botman/images/7)](https://sourcerer.io/fame/sergey48k/botman/botman/links/7) + ## Security Vulnerabilities If you discover a security vulnerability within BotMan, please send an e-mail to Marcel Pociot at m.pociot@gmail.com. All security vulnerabilities will be promptly addressed. diff --git a/vendor/botman/botman/composer.json b/vendor/botman/botman/composer.json index 9ec926e..ecfa774 100644 --- a/vendor/botman/botman/composer.json +++ b/vendor/botman/botman/composer.json @@ -20,7 +20,7 @@ "tightenco/collect": "~5.0", "opis/closure": "^2.3 || ^3.0", "mpociot/pipeline": "^1.0", - "react/socket": "~0.4", + "react/socket": "~1.0", "spatie/macroable": "^1.0", "psr/container": "^1.0" }, diff --git a/vendor/botman/botman/src/BotMan.php b/vendor/botman/botman/src/BotMan.php index c5b0fd5..73af650 100644 --- a/vendor/botman/botman/src/BotMan.php +++ b/vendor/botman/botman/src/BotMan.php @@ -26,6 +26,7 @@ use BotMan\BotMan\Traits\HandlesConversations; use Symfony\Component\HttpFoundation\Response; use BotMan\BotMan\Commands\ConversationManager; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Middleware\MiddlewareManager; use BotMan\BotMan\Messages\Attachments\Location; use BotMan\BotMan\Exceptions\Base\BotManException; @@ -337,6 +338,18 @@ public function receivesLocation($callback) return $this->hears(Location::PATTERN, $callback); } + /** + * Listening for contact attachment. + * + * @param $callback + * + * @return Command + */ + public function receivesContact($callback) + { + return $this->hears(Contact::PATTERN, $callback); + } + /** * Listening for files attachment. * @@ -687,7 +700,7 @@ protected function getCallable($callback) $callback = $this->makeInvokableAction($callback); } - list($class, $method) = explode('@', $callback); + [$class, $method] = explode('@', $callback); $command = $this->container ? $this->container->get($class) : new $class($this); diff --git a/vendor/botman/botman/src/Commands/ConversationManager.php b/vendor/botman/botman/src/Commands/ConversationManager.php index 3434a7d..db3a263 100644 --- a/vendor/botman/botman/src/Commands/ConversationManager.php +++ b/vendor/botman/botman/src/Commands/ConversationManager.php @@ -10,6 +10,7 @@ use BotMan\BotMan\Messages\Attachments\Audio; use BotMan\BotMan\Messages\Attachments\Image; use BotMan\BotMan\Messages\Attachments\Video; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Middleware\MiddlewareManager; use BotMan\BotMan\Messages\Attachments\Location; use BotMan\BotMan\Messages\Incoming\IncomingMessage; @@ -48,6 +49,8 @@ public function addDataParameters(IncomingMessage $message, array $parameters) $parameters[] = $message->getAudio(); } elseif ($messageText === Location::PATTERN) { $parameters[] = $message->getLocation(); + } elseif ($messageText === Contact::PATTERN) { + $parameters[] = $message->getContact(); } elseif ($messageText === File::PATTERN) { $parameters[] = $message->getFiles(); } diff --git a/vendor/botman/botman/src/Messages/Attachments/Contact.php b/vendor/botman/botman/src/Messages/Attachments/Contact.php new file mode 100644 index 0000000..4240f21 --- /dev/null +++ b/vendor/botman/botman/src/Messages/Attachments/Contact.php @@ -0,0 +1,118 @@ +phone_number = $phone_number; + $this->first_name = $first_name; + $this->last_name = $last_name; + $this->user_id = $user_id; + $this->vcard = $vcard; + } + + /** + * @param string $phone_number + * @param string $first_name + * @param string $last_name + * @param string $user_id + * @param string $vcard + * + * @return Contact + */ + public static function create($phone_number, $first_name, $last_name, $user_id, $vcard = null) + { + return new self($phone_number, $first_name, $last_name, $user_id, $vcard); + } + + /** + * @return string + */ + public function getPhoneNumber() + { + return $this->phone_number; + } + + /** + * @return string + */ + public function getFirstName() + { + return $this->first_name; + } + + /** + * @return string + */ + public function getLastName() + { + return $this->last_name; + } + + /** + * @return string + */ + public function getUserId() + { + return $this->user_id; + } + + /** + * @return string + */ + public function getVcard() + { + return $this->vcard; + } + + /** + * Get the instance as a web accessible array. + * This will be used within the WebDriver. + * + * @return array + */ + public function toWebDriver() + { + return [ + 'type' => 'contact', + 'phone_number' => $this->phone_number, + 'first_name' => $this->first_name, + 'last_name' => $this->last_name, + 'user_id' => $this->user_id, + 'vcard' => $this->vcard, + ]; + } +} diff --git a/vendor/botman/botman/src/Messages/Conversations/Conversation.php b/vendor/botman/botman/src/Messages/Conversations/Conversation.php index 176340f..45ee6c4 100644 --- a/vendor/botman/botman/src/Messages/Conversations/Conversation.php +++ b/vendor/botman/botman/src/Messages/Conversations/Conversation.php @@ -11,6 +11,7 @@ use BotMan\BotMan\Messages\Attachments\Image; use BotMan\BotMan\Messages\Attachments\Video; use BotMan\BotMan\Messages\Outgoing\Question; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Messages\Attachments\Location; use BotMan\BotMan\Messages\Incoming\IncomingMessage; @@ -83,6 +84,22 @@ public function askForImages($question, $next, $repeat = null, $additionalParame return $this->ask($question, $next, $additionalParameters); } + /** + * @param string|\BotMan\BotMan\Messages\Outgoing\Question $question + * @param array|Closure $next + * @param array|Closure $repeat + * @param array $additionalParameters + * @return $this + */ + public function askForFiles($question, $next, $repeat = null, $additionalParameters = []) + { + $additionalParameters['__getter'] = 'getFiles'; + $additionalParameters['__pattern'] = File::PATTERN; + $additionalParameters['__repeat'] = ! is_null($repeat) ? $this->bot->serializeClosure($repeat) : $repeat; + + return $this->ask($question, $next, $additionalParameters); + } + /** * @param string|\BotMan\BotMan\Messages\Outgoing\Question $question * @param array|Closure $next @@ -131,6 +148,23 @@ public function askForLocation($question, $next, $repeat = null, $additionalPara return $this->ask($question, $next, $additionalParameters); } + /** + * @param string|\BotMan\BotMan\Messages\Outgoing\Question $question + * @param array|Closure $next + * @param array|Closure $repeat + * @param array $additionalParameters + * + * @return $this + */ + public function askForContact($question, $next, $repeat = null, $additionalParameters = []) + { + $additionalParameters['__getter'] = 'getContact'; + $additionalParameters['__pattern'] = Contact::PATTERN; + $additionalParameters['__repeat'] = ! is_null($repeat) ? $this->bot->serializeClosure($repeat) : $repeat; + + return $this->ask($question, $next, $additionalParameters); + } + /** * Repeat the previously asked question. * @param string|Question $question diff --git a/vendor/botman/botman/src/Messages/Incoming/IncomingMessage.php b/vendor/botman/botman/src/Messages/Incoming/IncomingMessage.php index cb5d36e..18f66b2 100644 --- a/vendor/botman/botman/src/Messages/Incoming/IncomingMessage.php +++ b/vendor/botman/botman/src/Messages/Incoming/IncomingMessage.php @@ -3,6 +3,7 @@ namespace BotMan\BotMan\Messages\Incoming; use Illuminate\Support\Collection; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Messages\Attachments\Location; class IncomingMessage @@ -37,6 +38,9 @@ class IncomingMessage /** @var \BotMan\BotMan\Messages\Attachments\Location */ private $location; + /** @var \BotMan\BotMan\Messages\Attachments\Contact */ + private $contact; + /** @var bool */ protected $isFromBot = false; @@ -210,6 +214,26 @@ public function getLocation() : Location return $this->location; } + /** + * @return \BotMan\BotMan\Messages\Attachments\Contact + */ + public function getContact() : Contact + { + if (empty($this->contact)) { + throw new \UnexpectedValueException('This message does not contain a contact'); + } + + return $this->contact; + } + + /** + * @param \BotMan\BotMan\Messages\Attachments\Contact $contact + */ + public function setContact(Contact $contact) + { + $this->contact = $contact; + } + /** * @return bool */ diff --git a/vendor/botman/botman/src/Middleware/ApiAi.php b/vendor/botman/botman/src/Middleware/ApiAi.php index ba1869c..7137110 100644 --- a/vendor/botman/botman/src/Middleware/ApiAi.php +++ b/vendor/botman/botman/src/Middleware/ApiAi.php @@ -120,12 +120,23 @@ public function received(IncomingMessage $message, $next, BotMan $bot) $actionIncomplete = isset($response->result->actionIncomplete) ? (bool) $response->result->actionIncomplete : false; $intent = $response->result->metadata->intentName ?? ''; $parameters = isset($response->result->parameters) ? (array) $response->result->parameters : []; + $responseMessages = isset($response->result->fulfillment->messages) ? json_decode(json_encode($response->result->fulfillment->messages), true) : []; + $contexts = isset($response->result->contexts) ? json_decode(json_encode($response->result->contexts), true) : []; + + if (count($responseMessages)) { + $textResponses = count(collect($responseMessages)->where('type', 0)) ? array_values(collect($responseMessages)->where('type', 0)->toArray()) : []; + $customPayloadResponses = count(collect($responseMessages)->where('type', 4)) ? array_values(collect($responseMessages)->where('type', 4)->toArray()) : []; + } $message->addExtras('apiReply', $reply); $message->addExtras('apiAction', $action); $message->addExtras('apiActionIncomplete', $actionIncomplete); $message->addExtras('apiIntent', $intent); $message->addExtras('apiParameters', $parameters); + $message->addExtras('apiResponseMessages', $responseMessages); + $message->addExtras('apiTextResponses', $textResponses ?? []); + $message->addExtras('apiCustomPayloadResponses', $customPayloadResponses ?? []); + $message->addExtras('apiContexts', $contexts); return $next($message); } diff --git a/vendor/botman/driver-facebook/src/Events/MessagingAccountLinking.php b/vendor/botman/driver-facebook/src/Events/MessagingAccountLinking.php new file mode 100644 index 0000000..4550968 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Events/MessagingAccountLinking.php @@ -0,0 +1,16 @@ +locale = $locale; + } + + /** + * @param string $themeColor + * + * @return \BotMan\Drivers\Facebook\Extensions\AbstractAirlineTemplate + */ + public function themeColor(string $themeColor): self + { + $this->themeColor = $themeColor; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'attachment' => [ + 'type' => 'template', + 'payload' => [ + 'locale' => $this->locale, + 'theme_color' => $this->themeColor, + ], + ], + ]; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } + + /** + * Get the instance as a web accessible array. + * This will be used within the WebDriver. + * + * @return array + */ + public function toWebDriver(): array + { + return [ + 'locale' => $this->locale, + 'theme_color' => $this->themeColor, + ]; + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AbstractAirlineFlightInfo.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AbstractAirlineFlightInfo.php new file mode 100644 index 0000000..5c8d436 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AbstractAirlineFlightInfo.php @@ -0,0 +1,70 @@ +flightNumber = $flightNumber; + $this->departureAirport = $departureAirport; + $this->arrivalAirport = $arrivalAirport; + $this->flightSchedule = $flightSchedule; + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'flight_number' => $this->flightNumber, + 'departure_airport' => $this->departureAirport, + 'arrival_airport' => $this->arrivalAirport, + 'flight_schedule' => $this->flightSchedule, + ]; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineAirport.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineAirport.php new file mode 100644 index 0000000..baf768f --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineAirport.php @@ -0,0 +1,98 @@ +airportCode = $airportCode; + $this->city = $city; + } + + /** + * @param string $terminal + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineAirport + */ + public function terminal(string $terminal): self + { + $this->terminal = $terminal; + + return $this; + } + + /** + * @param string $gate + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineAirport + */ + public function gate(string $gate): self + { + $this->gate = $gate; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = [ + 'airport_code' => $this->airportCode, + 'city' => $this->city, + 'terminal' => $this->terminal, + 'gate' => $this->gate, + ]; + + return array_filter($array); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineBoardingPass.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineBoardingPass.php new file mode 100644 index 0000000..4f05836 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineBoardingPass.php @@ -0,0 +1,292 @@ +passengerName = $passengerName; + $this->pnrNumber = $pnrNumber; + $this->logoImageUrl = $logoImageUrl; + $this->aboveBarcodeImageUrl = $aboveBarcodeImageUrl; + $this->flightInfo = $flightInfo; + + $this->setCode($code); + } + + /** + * @param string $travelClass + * + * @throws \BotMan\Drivers\Facebook\Exceptions\FacebookException + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function travelClass(string $travelClass): self + { + if (! \in_array($travelClass, self::TRAVEL_TYPES, true)) { + throw new FacebookException( + sprintf('travel_class must be either "%s"', implode(', ', self::TRAVEL_TYPES)) + ); + } + + $this->travelClass = $travelClass; + + return $this; + } + + /** + * @param string $seat + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function seat(string $seat): self + { + $this->seat = $seat; + + return $this; + } + + /** + * @param string $label + * @param string $value + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function addAuxiliaryField(string $label, string $value): self + { + $this->auxiliaryFields[] = $this->setLabelValue($label, $value); + + return $this; + } + + /** + * @param array $auxiliaryFields + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function addAuxiliaryFields(array $auxiliaryFields): self + { + foreach ($auxiliaryFields as $label => $value) { + $this->auxiliaryFields[] = $this->setLabelValue($label, $value); + } + + return $this; + } + + /** + * @param string $label + * @param string $value + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function addSecondaryField(string $label, string $value): self + { + $this->secondaryFields[] = $this->setLabelValue($label, $value); + + return $this; + } + + /** + * @param array $secondaryFields + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function addSecondaryFields(array $secondaryFields): self + { + foreach ($secondaryFields as $label => $value) { + $this->secondaryFields[] = $this->setLabelValue($label, $value); + } + + return $this; + } + + /** + * @param string $headerImageUrl + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function headerImageUrl(string $headerImageUrl): self + { + $this->headerImageUrl = $headerImageUrl; + + return $this; + } + + /** + * @param string $headerTextField + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass + */ + public function headerTextField(string $headerTextField): self + { + $this->headerTextField = $headerTextField; + + return $this; + } + + /** + * @param string $code + */ + private function setCode(string $code) + { + if (filter_var($code, FILTER_VALIDATE_URL)) { + $this->barcodeImageUrl = $code; + + return; + } + $this->qrCode = $code; + } + + /** + * @param string $label + * @param string $value + * + * @return array + */ + private function setLabelValue(string $label, string $value): array + { + return [ + 'label' => $label, + 'value' => $value, + ]; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = [ + 'passenger_name' => $this->passengerName, + 'pnr_number' => $this->pnrNumber, + 'travel_class' => $this->travelClass, + 'seat' => $this->seat, + 'auxiliary_fields' => $this->auxiliaryFields, + 'secondary_fields' => $this->secondaryFields, + 'logo_image_url' => $this->logoImageUrl, + 'header_image_url' => $this->headerImageUrl, + 'header_text_field' => $this->headerTextField, + 'qr_code' => $this->qrCode, + 'barcode_image_url' => $this->barcodeImageUrl, + 'above_bar_code_image_url' => $this->aboveBarcodeImageUrl, + 'flight_info' => $this->flightInfo, + ]; + + return array_filter($array); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineExtendedFlightInfo.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineExtendedFlightInfo.php new file mode 100644 index 0000000..2ffd70e --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineExtendedFlightInfo.php @@ -0,0 +1,124 @@ +connectionId = $connectionId; + $this->segmentId = $segmentId; + $this->travelClass = $travelClass; + } + + /** + * @param string $aircraftType + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineExtendedFlightInfo + */ + public function aircraftType(string $aircraftType): self + { + $this->aircraftType = $aircraftType; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = parent::toArray(); + $array += [ + 'connection_id' => $this->connectionId, + 'segment_id' => $this->segmentId, + 'travel_class' => $this->travelClass, + 'aircraft_type' => $this->aircraftType, + ]; + + return array_filter($array); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineFlightInfo.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineFlightInfo.php new file mode 100644 index 0000000..c49fb65 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlineFlightInfo.php @@ -0,0 +1,23 @@ +departureTime = $departureTime; + } + + /** + * @param string $boardingTime + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineFlightSchedule + */ + public function boardingTime(string $boardingTime): self + { + $this->boardingTime = $boardingTime; + + return $this; + } + + /** + * @param string $arrivalTime + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlineFlightSchedule + */ + public function arrivalTime(string $arrivalTime): self + { + $this->arrivalTime = $arrivalTime; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = [ + 'boarding_time' => $this->boardingTime, + 'departure_time' => $this->departureTime, + 'arrival_time' => $this->arrivalTime, + ]; + + return array_filter($array); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerInfo.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerInfo.php new file mode 100644 index 0000000..ce743cb --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerInfo.php @@ -0,0 +1,80 @@ +passengerId = $passengerId; + $this->name = $name; + } + + /** + * @param string $ticketNumber + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlinePassengerInfo + */ + public function ticketNumber(string $ticketNumber): self + { + $this->ticketNumber = $ticketNumber; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = [ + 'passenger_id' => $this->passengerId, + 'ticket_number' => $this->ticketNumber, + 'name' => $this->name, + ]; + + return array_filter($array); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerSegmentInfo.php b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerSegmentInfo.php new file mode 100644 index 0000000..82984cc --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/Airline/AirlinePassengerSegmentInfo.php @@ -0,0 +1,119 @@ +segmentId = $segmentId; + $this->passengerId = $passengerId; + $this->seat = $seat; + $this->seatType = $seatType; + } + + /** + * @param string $title + * @param string $value + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlinePassengerSegmentInfo + */ + public function addProductInfo(string $title, string $value): self + { + $this->productInfo[] = [ + 'title' => $title, + 'value' => $value, + ]; + + return $this; + } + + /** + * @param array $productsInfo + * + * @return \BotMan\Drivers\Facebook\Extensions\Airline\AirlinePassengerSegmentInfo + */ + public function addProductsInfo(array $productsInfo): self + { + foreach ($productsInfo as $title => $value) { + $this->productInfo[] = [ + 'title' => $title, + 'value' => $value, + ]; + } + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = [ + 'segment_id' => $this->segmentId, + 'passenger_id' => $this->passengerId, + 'seat' => $this->seat, + 'seat_type' => $this->seatType, + 'product_info' => $this->productInfo, + ]; + + return array_filter($array); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/AirlineBoardingPassTemplate.php b/vendor/botman/driver-facebook/src/Extensions/AirlineBoardingPassTemplate.php new file mode 100644 index 0000000..f49253e --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/AirlineBoardingPassTemplate.php @@ -0,0 +1,77 @@ +introMessage = $introMessage; + $this->boardingPass = $boardingPass; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = parent::toArray(); + + return array_merge_recursive($array, [ + 'attachment' => [ + 'payload' => [ + 'template_type' => 'airline_boardingpass', + 'intro_message' => $this->introMessage, + 'boarding_pass' => $this->boardingPass, + ], + ], + ]); + } + + /** + * Get the instance as a web accessible array. + * This will be used within the WebDriver. + * + * @return array + */ + public function toWebDriver(): array + { + $webDriver = parent::toWebDriver(); + $webDriver += [ + 'type' => 'airline_boardingpass', + 'intro_message' => $this->introMessage, + 'boarding_pass' => $this->boardingPass, + ]; + + return $webDriver; + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/AirlineCheckInTemplate.php b/vendor/botman/driver-facebook/src/Extensions/AirlineCheckInTemplate.php new file mode 100644 index 0000000..1834999 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/AirlineCheckInTemplate.php @@ -0,0 +1,109 @@ +introMessage = $introMessage; + $this->pnrNumber = $pnrNumber; + $this->flightInfo = $flightInfo; + $this->checkinUrl = $checkinUrl; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = parent::toArray(); + + return array_merge_recursive($array, [ + 'attachment' => [ + 'payload' => [ + 'template_type' => 'airline_checkin', + 'intro_message' => $this->introMessage, + 'pnr_number' => $this->pnrNumber, + 'flight_info' => $this->flightInfo, + 'checkin_url' => $this->checkinUrl, + ], + ], + ]); + } + + /** + * Get the instance as a web accessible array. + * This will be used within the WebDriver. + * + * @return array + */ + public function toWebDriver(): array + { + $webDriver = parent::toWebDriver(); + $webDriver += [ + 'type' => 'airline_checkin', + 'intro_message' => $this->introMessage, + 'pnr_number' => $this->pnrNumber, + 'flight_info' => $this->flightInfo, + 'checkin_url' => $this->checkinUrl, + ]; + + return $webDriver; + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/AirlineItineraryTemplate.php b/vendor/botman/driver-facebook/src/Extensions/AirlineItineraryTemplate.php new file mode 100644 index 0000000..4463182 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/AirlineItineraryTemplate.php @@ -0,0 +1,219 @@ +introMessage = $introMessage; + $this->pnrNumber = $pnrNumber; + $this->passengerInfo = $passengerInfo; + $this->flightInfo = $flightInfo; + $this->passengerSegmentInfo = $passengerSegmentInfo; + $this->totalPrice = $totalPrice; + $this->currency = $currency; + } + + /** + * @param string $title + * @param string $amount + * @param string|null $currency + * + * @return \BotMan\Drivers\Facebook\Extensions\AirlineItineraryTemplate + */ + public function addPriceInfo(string $title, string $amount, string $currency = null): self + { + $priceInfo = [ + 'title' => $title, + 'amount' => $amount, + 'currency' => $currency, + ]; + + $this->priceInfo[] = array_filter($priceInfo); + + return $this; + } + + /** + * @param string $basePrice + * + * @return \BotMan\Drivers\Facebook\Extensions\AirlineItineraryTemplate + */ + public function basePrice(string $basePrice): self + { + $this->basePrice = $basePrice; + + return $this; + } + + /** + * @param string $tax + * + * @return \BotMan\Drivers\Facebook\Extensions\AirlineItineraryTemplate + */ + public function tax(string $tax): self + { + $this->tax = $tax; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = parent::toArray(); + + return array_merge_recursive($array, [ + 'attachment' => [ + 'payload' => [ + 'template_type' => 'airline_itinerary', + 'intro_message' => $this->introMessage, + 'pnr_number' => $this->pnrNumber, + 'passenger_info' => $this->passengerInfo, + 'flight_info' => $this->flightInfo, + 'passenger_segment_info' => $this->passengerSegmentInfo, + 'price_info' => $this->priceInfo, + 'base_price' => $this->basePrice, + 'tax' => $this->tax, + 'total_price' => $this->totalPrice, + 'currency' => $this->currency, + ], + ], + ]); + } + + /** + * Get the instance as a web accessible array. + * This will be used within the WebDriver. + * + * @return array + */ + public function toWebDriver(): array + { + $webDriver = parent::toWebDriver(); + $webDriver += [ + 'template_type' => 'airline_itinerary', + 'intro_message' => $this->introMessage, + 'pnr_number' => $this->pnrNumber, + 'passenger_info' => $this->passengerInfo, + 'flight_info' => $this->flightInfo, + 'passenger_segment_info' => $this->passengerSegmentInfo, + 'price_info' => $this->priceInfo, + 'base_price' => $this->basePrice, + 'tax' => $this->tax, + 'total_price' => $this->totalPrice, + 'currency' => $this->currency, + ]; + + return $webDriver; + } +} diff --git a/vendor/botman/driver-facebook/src/Extensions/AirlineUpdateTemplate.php b/vendor/botman/driver-facebook/src/Extensions/AirlineUpdateTemplate.php new file mode 100644 index 0000000..520ce3c --- /dev/null +++ b/vendor/botman/driver-facebook/src/Extensions/AirlineUpdateTemplate.php @@ -0,0 +1,111 @@ +updateType = $updateType; + $this->locale = $locale; + $this->pnrNumber = $pnrNumber; + $this->updateFlightInfo = $updateFlightInfo; + } + + /** + * @param string $introMessage + * + * @return \BotMan\Drivers\Facebook\Extensions\AirlineUpdateTemplate + */ + public function introMessage(string $introMessage): self + { + $this->introMessage = $introMessage; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = parent::toArray(); + + return array_merge_recursive($array, [ + 'attachment' => [ + 'payload' => [ + 'template_type' => 'airline_update', + 'intro_message' => $this->introMessage, + 'update_type' => $this->updateType, + 'pnr_number' => $this->pnrNumber, + 'update_flight_info' => $this->updateFlightInfo, + ], + ], + ]); + } + + /** + * Get the instance as a web accessible array. + * This will be used within the WebDriver. + * + * @return array + */ + public function toWebDriver(): array + { + $webDriver = parent::toWebDriver(); + $webDriver += [ + 'template_type' => 'airline_update', + 'intro_message' => $this->introMessage, + 'update_type' => $this->updateType, + 'pnr_number' => $this->pnrNumber, + 'update_flight_info' => $this->updateFlightInfo, + ]; + + return $webDriver; + } +} diff --git a/vendor/botman/driver-facebook/src/FacebookDriver.php b/vendor/botman/driver-facebook/src/FacebookDriver.php index d1d56d8..80d341e 100755 --- a/vendor/botman/driver-facebook/src/FacebookDriver.php +++ b/vendor/botman/driver-facebook/src/FacebookDriver.php @@ -30,6 +30,10 @@ use BotMan\Drivers\Facebook\Extensions\ReceiptTemplate; use BotMan\Drivers\Facebook\Exceptions\FacebookException; use BotMan\Drivers\Facebook\Extensions\OpenGraphTemplate; +use BotMan\Drivers\Facebook\Extensions\AirlineUpdateTemplate; +use BotMan\Drivers\Facebook\Extensions\AirlineCheckInTemplate; +use BotMan\Drivers\Facebook\Extensions\AirlineItineraryTemplate; +use BotMan\Drivers\Facebook\Extensions\Airline\AirlineBoardingPass; class FacebookDriver extends HttpDriver implements VerifiesService { @@ -50,6 +54,10 @@ class FacebookDriver extends HttpDriver implements VerifiesService /** @var array */ protected $templates = [ + AirlineBoardingPass::class, + AirlineCheckInTemplate::class, + AirlineItineraryTemplate::class, + AirlineUpdateTemplate::class, ButtonTemplate::class, GenericTemplate::class, ListTemplate::class, @@ -165,6 +173,9 @@ protected function getEventFromEventData(array $eventData) case 'read': return new MessagingReads($eventData); break; + case 'account_linking': + return new Events\MessagingAccountLinking($eventData); + break; case 'checkout_update': return new Events\MessagingCheckoutUpdates($eventData); break; @@ -228,7 +239,7 @@ public function getConversationAnswer(IncomingMessage $message) { $payload = $message->getPayload(); if (isset($payload['message']['quick_reply'])) { - return Answer::create($message->getText())->setMessage($message)->setInteractiveReply(true)->setValue($payload['message']['quick_reply']['payload']); + return Answer::create($payload['message']['text'])->setMessage($message)->setInteractiveReply(true)->setValue($payload['message']['quick_reply']['payload']); } elseif (isset($payload['postback']['payload'])) { return Answer::create($payload['postback']['title'])->setMessage($message)->setInteractiveReply(true)->setValue($payload['postback']['payload']); } diff --git a/vendor/botman/driver-facebook/src/Interfaces/Airline.php b/vendor/botman/driver-facebook/src/Interfaces/Airline.php new file mode 100644 index 0000000..e28abf1 --- /dev/null +++ b/vendor/botman/driver-facebook/src/Interfaces/Airline.php @@ -0,0 +1,26 @@ +apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 3a14165..7a91153 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,11 +6,4 @@ $baseDir = dirname($vendorDir); return array( - 'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', - 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', - 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', - 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', - 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', - 'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', - 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', ); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 2dc20cc..2d5252e 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -7,9 +7,10 @@ return array( 'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php', - '6b06ce8ccf69c43a60a1e48495a034c9' => $vendorDir . '/react/promise-timer/src/functions.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', - '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', + '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', + '972fda704d680a3a53c68e34e193cb22' => $vendorDir . '/react/promise-timer/src/functions_include.php', + 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', '538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php', 'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index da53575..c13ede5 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -7,9 +7,11 @@ return array( 'Tightenco\\Collect\\' => array($vendorDir . '/tightenco/collect/src/Collect'), - 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), + 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'), 'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), + 'Symfony\\Component\\Mime\\' => array($vendorDir . '/symfony/mime'), 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), 'Spatie\\Macroable\\' => array($vendorDir . '/spatie/macroable/src'), 'React\\Stream\\' => array($vendorDir . '/react/stream/src'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 87f9eee..91ec106 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -8,9 +8,10 @@ class ComposerStaticInitf210d6705b6a45c0d651c7456aefe483 { public static $files = array ( 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', - '6b06ce8ccf69c43a60a1e48495a034c9' => __DIR__ . '/..' . '/react/promise-timer/src/functions.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', - '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', + '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', + '972fda704d680a3a53c68e34e193cb22' => __DIR__ . '/..' . '/react/promise-timer/src/functions_include.php', + 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', '538ca81a9a966a6716601ecf48f4eaef' => __DIR__ . '/..' . '/opis/closure/functions.php', 'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php', @@ -24,9 +25,11 @@ class ComposerStaticInitf210d6705b6a45c0d651c7456aefe483 ), 'S' => array ( - 'Symfony\\Polyfill\\Php70\\' => 23, + 'Symfony\\Polyfill\\Php72\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Intl\\Idn\\' => 26, 'Symfony\\Component\\VarDumper\\' => 28, + 'Symfony\\Component\\Mime\\' => 23, 'Symfony\\Component\\HttpFoundation\\' => 33, 'Spatie\\Macroable\\' => 17, ), @@ -64,18 +67,26 @@ class ComposerStaticInitf210d6705b6a45c0d651c7456aefe483 array ( 0 => __DIR__ . '/..' . '/tightenco/collect/src/Collect', ), - 'Symfony\\Polyfill\\Php70\\' => + 'Symfony\\Polyfill\\Php72\\' => array ( - 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', + 0 => __DIR__ . '/..' . '/symfony/polyfill-php72', ), 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), + 'Symfony\\Polyfill\\Intl\\Idn\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn', + ), 'Symfony\\Component\\VarDumper\\' => array ( 0 => __DIR__ . '/..' . '/symfony/var-dumper', ), + 'Symfony\\Component\\Mime\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/mime', + ), 'Symfony\\Component\\HttpFoundation\\' => array ( 0 => __DIR__ . '/..' . '/symfony/http-foundation', @@ -144,23 +155,12 @@ class ComposerStaticInitf210d6705b6a45c0d651c7456aefe483 ), ); - public static $classMap = array ( - 'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', - 'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', - 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', - 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', - 'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', - 'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', - 'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', - ); - public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInitf210d6705b6a45c0d651c7456aefe483::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInitf210d6705b6a45c0d651c7456aefe483::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInitf210d6705b6a45c0d651c7456aefe483::$prefixesPsr0; - $loader->classMap = ComposerStaticInitf210d6705b6a45c0d651c7456aefe483::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 8f7b968..32ff2e0 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,17 +1,17 @@ [ { "name": "botman/botman", - "version": "2.4.1", - "version_normalized": "2.4.1.0", + "version": "2.5.0", + "version_normalized": "2.5.0.0", "source": { "type": "git", "url": "https://github.com/botman/botman.git", - "reference": "b4d07814c5848fce1e841bba836c7d1a3ee51d29" + "reference": "c49cacb56f0b30178986f77e4fbc572b56c2e7bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/botman/botman/zipball/b4d07814c5848fce1e841bba836c7d1a3ee51d29", - "reference": "b4d07814c5848fce1e841bba836c7d1a3ee51d29", + "url": "https://api.github.com/repos/botman/botman/zipball/c49cacb56f0b30178986f77e4fbc572b56c2e7bc", + "reference": "c49cacb56f0b30178986f77e4fbc572b56c2e7bc", "shasum": "" }, "require": { @@ -19,7 +19,7 @@ "opis/closure": "^2.3 || ^3.0", "php": ">=7.0", "psr/container": "^1.0", - "react/socket": "~0.4", + "react/socket": "~1.0", "spatie/macroable": "^1.0", "symfony/http-foundation": "^2.8 || ^3.0 || ^4.0", "tightenco/collect": "~5.0" @@ -39,7 +39,7 @@ "doctrine/cache": "Use any Doctrine cache driver for cache", "symfony/cache": "Use any Symfony cache driver for cache" }, - "time": "2018-08-09T13:15:19+00:00", + "time": "2019-05-14T21:18:37+00:00", "type": "library", "extra": { "branch-alias": { @@ -80,17 +80,17 @@ }, { "name": "botman/driver-facebook", - "version": "1.9.4", - "version_normalized": "1.9.4.0", + "version": "1.10.0", + "version_normalized": "1.10.0.0", "source": { "type": "git", "url": "https://github.com/botman/driver-facebook.git", - "reference": "8071f777388de7cde8122a340acdab01694d517b" + "reference": "a02096c5b9dafddc3353e8c5eff060fcb004985f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/botman/driver-facebook/zipball/8071f777388de7cde8122a340acdab01694d517b", - "reference": "8071f777388de7cde8122a340acdab01694d517b", + "url": "https://api.github.com/repos/botman/driver-facebook/zipball/a02096c5b9dafddc3353e8c5eff060fcb004985f", + "reference": "a02096c5b9dafddc3353e8c5eff060fcb004985f", "shasum": "" }, "require": { @@ -104,7 +104,7 @@ "mockery/mockery": "dev-master", "phpunit/phpunit": "~5.0" }, - "time": "2018-10-04T06:45:12+00:00", + "time": "2019-03-19T06:36:25+00:00", "type": "library", "extra": { "branch-alias": { @@ -235,17 +235,17 @@ }, { "name": "opis/closure", - "version": "3.1.5", - "version_normalized": "3.1.5.0", + "version": "3.5.1", + "version_normalized": "3.5.1.0", "source": { "type": "git", "url": "https://github.com/opis/closure.git", - "reference": "41f5da65d75cf473e5ee582df8fc7f2c733ce9d6" + "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/41f5da65d75cf473e5ee582df8fc7f2c733ce9d6", - "reference": "41f5da65d75cf473e5ee582df8fc7f2c733ce9d6", + "url": "https://api.github.com/repos/opis/closure/zipball/93ebc5712cdad8d5f489b500c59d122df2e53969", + "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969", "shasum": "" }, "require": { @@ -253,13 +253,13 @@ }, "require-dev": { "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0|^5.0|^6.0|^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, - "time": "2019-01-14T14:45:33+00:00", + "time": "2019-11-29T22:36:02+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "3.5.x-dev" } }, "installation-source": "dist", @@ -296,53 +296,6 @@ "serialize" ] }, - { - "name": "paragonie/random_compat", - "version": "v9.99.99", - "version_normalized": "9.99.99.0", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "shasum": "" - }, - "require": { - "php": "^7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "time": "2018-07-02T15:55:56+00:00", - "type": "library", - "installation-source": "dist", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ] - }, { "name": "psr/container", "version": "1.0.0", @@ -396,17 +349,17 @@ }, { "name": "react/cache", - "version": "v0.5.0", - "version_normalized": "0.5.0.0", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", "source": { "type": "git", "url": "https://github.com/reactphp/cache.git", - "reference": "7d7da7fb7574d471904ba357b39bbf110ccdbf66" + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/7d7da7fb7574d471904ba357b39bbf110ccdbf66", - "reference": "7d7da7fb7574d471904ba357b39bbf110ccdbf66", + "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", + "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", "shasum": "" }, "require": { @@ -416,7 +369,7 @@ "require-dev": { "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" }, - "time": "2018-06-25T12:52:40+00:00", + "time": "2019-07-11T13:45:28+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -438,32 +391,31 @@ }, { "name": "react/dns", - "version": "v0.4.16", - "version_normalized": "0.4.16.0", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "0a0bedfec72b38406413c6ea01e1c015bd0bf72b" + "reference": "a214d90c2884dac18d0cac6176202f247b66d762" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/0a0bedfec72b38406413c6ea01e1c015bd0bf72b", - "reference": "0a0bedfec72b38406413c6ea01e1c015bd0bf72b", + "url": "https://api.github.com/repos/reactphp/dns/zipball/a214d90c2884dac18d0cac6176202f247b66d762", + "reference": "a214d90c2884dac18d0cac6176202f247b66d762", "shasum": "" }, "require": { "php": ">=5.3.0", - "react/cache": "^0.5 || ^0.4 || ^0.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.1 || ^1.2.1", - "react/promise-timer": "^1.2", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5", + "react/promise": "^2.7 || ^1.2.1", + "react/promise-timer": "^1.2" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, - "time": "2018-11-11T11:21:13+00:00", + "time": "2019-08-15T09:06:31+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -485,30 +437,31 @@ }, { "name": "react/event-loop", - "version": "v1.0.0", - "version_normalized": "1.0.0.0", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "0266aff7aa7b0613b1f38a723e14a0ebc55cfca3" + "reference": "6d24de090cd59cfc830263cfba965be77b563c13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/0266aff7aa7b0613b1f38a723e14a0ebc55cfca3", - "reference": "0266aff7aa7b0613b1f38a723e14a0ebc55cfca3", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13", + "reference": "6d24de090cd59cfc830263cfba965be77b563c13", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "suggest": { "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" }, - "time": "2018-07-11T14:37:46+00:00", + "time": "2020-01-01T18:39:52+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -576,17 +529,17 @@ }, { "name": "react/promise-timer", - "version": "v1.5.0", - "version_normalized": "1.5.0.0", + "version": "v1.5.1", + "version_normalized": "1.5.1.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise-timer.git", - "reference": "a11206938ca2394dc7bb368f5da25cd4533fa603" + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/a11206938ca2394dc7bb368f5da25cd4533fa603", - "reference": "a11206938ca2394dc7bb368f5da25cd4533fa603", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", "shasum": "" }, "require": { @@ -597,7 +550,7 @@ "require-dev": { "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" }, - "time": "2018-06-13T16:45:37+00:00", + "time": "2019-03-27T18:10:32+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -605,7 +558,7 @@ "React\\Promise\\Timer\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -631,33 +584,34 @@ }, { "name": "react/socket", - "version": "v0.8.12", - "version_normalized": "0.8.12.0", + "version": "v1.4.0", + "version_normalized": "1.4.0.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c" + "reference": "97522e24987365e1ed873f0f4884900747a668e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/7f7e6c56ccda7418a1a264892a625f38a5bdee0c", - "reference": "7f7e6c56ccda7418a1a264892a625f38a5bdee0c", + "url": "https://api.github.com/repos/reactphp/socket/zipball/97522e24987365e1ed873f0f4884900747a668e0", + "reference": "97522e24987365e1ed873f0f4884900747a668e0", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "php": ">=5.3.0", - "react/dns": "^0.4.13", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/dns": "^1.1", + "react/event-loop": "^1.0 || ^0.5", "react/promise": "^2.6.0 || ^1.2.1", "react/promise-timer": "^1.4.0", - "react/stream": "^1.0 || ^0.7.1" + "react/stream": "^1.1" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.5 || ^6.4 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" }, - "time": "2018-06-11T14:33:43+00:00", + "time": "2020-03-12T12:15:14+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -776,32 +730,33 @@ }, { "name": "symfony/http-foundation", - "version": "v3.4.21", - "version_normalized": "3.4.21.0", + "version": "v4.4.5", + "version_normalized": "4.4.5.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "2b97319e68816d2120eee7f13f4b76da12e04d03" + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/2b97319e68816d2120eee7f13f4b76da12e04d03", - "reference": "2b97319e68816d2120eee7f13f4b76da12e04d03", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7e41b4fcad4619535f45f8bfa7744c4f384e1648", + "reference": "7e41b4fcad4619535f45f8bfa7744c4f384e1648", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php70": "~1.6" + "php": "^7.1.3", + "symfony/mime": "^4.3|^5.0", + "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" + "predis/predis": "~1.0", + "symfony/expression-language": "^3.4|^4.0|^5.0" }, - "time": "2019-01-05T08:05:37+00:00", + "time": "2020-02-13T19:40:01+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.4-dev" } }, "installation-source": "dist", @@ -830,19 +785,147 @@ "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com" }, + { + "name": "symfony/mime", + "version": "v5.0.5", + "version_normalized": "5.0.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10", + "symfony/dependency-injection": "^4.4|^5.0" + }, + "time": "2020-02-04T09:41:09+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A library to manipulate MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ] + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.14.0", + "version_normalized": "1.14.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6842f1a39cf7d580655688069a03dd7cd83d244a", + "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2020-01-17T12:01:36+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.14-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ] + }, { "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", - "version_normalized": "1.10.0.0", + "version": "v1.14.0", + "version_normalized": "1.14.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", "shasum": "" }, "require": { @@ -851,11 +934,11 @@ "suggest": { "ext-mbstring": "For best performance" }, - "time": "2018-09-21T13:07:52+00:00", + "time": "2020-01-13T11:15:53+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "installation-source": "dist", @@ -892,41 +975,37 @@ ] }, { - "name": "symfony/polyfill-php70", - "version": "v1.10.0", - "version_normalized": "1.10.0.0", + "name": "symfony/polyfill-php72", + "version": "v1.14.0", + "version_normalized": "1.14.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", "php": ">=5.3.3" }, - "time": "2018-09-21T06:26:08+00:00", + "time": "2020-01-13T11:15:53+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" + "Symfony\\Polyfill\\Php72\\": "" }, "files": [ "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -943,7 +1022,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -954,40 +1033,47 @@ }, { "name": "symfony/var-dumper", - "version": "v3.4.21", - "version_normalized": "3.4.21.0", + "version": "v4.4.5", + "version_normalized": "4.4.5.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2" + "reference": "2572839911702b0405479410ea7a1334bfab0b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a5f39641bb62e8b74e343467b145331273f615a2", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2572839911702b0405479410ea7a1334bfab0b96", + "reference": "2572839911702b0405479410ea7a1334bfab0b96", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.0" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/console": "<3.4" }, "require-dev": { "ext-iconv": "*", - "twig/twig": "~1.34|~2.4" + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^4.4|^5.0", + "twig/twig": "^1.34|^2.4|^3.0" }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", "ext-intl": "To show region name in time zone dump", - "ext-symfony_debug": "" + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" }, - "time": "2019-01-01T13:45:19+00:00", + "time": "2020-02-24T13:10:00+00:00", + "bin": [ + "Resources/bin/var-dump-server" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.4-dev" } }, "installation-source": "dist", @@ -1025,28 +1111,29 @@ }, { "name": "tightenco/collect", - "version": "v5.5.33", - "version_normalized": "5.5.33.0", + "version": "v5.8.35", + "version_normalized": "5.8.35.0", "source": { "type": "git", "url": "https://github.com/tightenco/collect.git", - "reference": "a2a3ca1c56aa18948b4e08e90f38fcdee859cc4c" + "reference": "c93a7039e6207ad533a09109838fe80933fcc72c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tightenco/collect/zipball/a2a3ca1c56aa18948b4e08e90f38fcdee859cc4c", - "reference": "a2a3ca1c56aa18948b4e08e90f38fcdee859cc4c", + "url": "https://api.github.com/repos/tightenco/collect/zipball/c93a7039e6207ad533a09109838fe80933fcc72c", + "reference": "c93a7039e6207ad533a09109838fe80933fcc72c", "shasum": "" }, "require": { - "php": ">=7.0", - "symfony/var-dumper": "~3.3" + "php": "^7.1.3", + "symfony/var-dumper": ">=3.4 <5" }, "require-dev": { - "mockery/mockery": "~1.0", - "phpunit/phpunit": "~6.0" + "mockery/mockery": "^1.0", + "nesbot/carbon": "^1.26.3", + "phpunit/phpunit": "^7.0" }, - "time": "2018-02-09T02:05:17+00:00", + "time": "2019-09-17T18:57:01+00:00", "type": "library", "installation-source": "dist", "autoload": { diff --git a/vendor/opis/closure/CHANGELOG.md b/vendor/opis/closure/CHANGELOG.md index 78b8219..44370bf 100644 --- a/vendor/opis/closure/CHANGELOG.md +++ b/vendor/opis/closure/CHANGELOG.md @@ -1,6 +1,52 @@ CHANGELOG --------- +### v3.5.1, 2019.11.30 + +- Bugfix. See #47 + +### v3.5.0, 2019.11.29 + +- Added support for short closures (arrow functions) +- Added `isShortClosure` method to `Opis\Closure\ReflectionClosure` + +### v3.4.2, 2019.11.29 + +- Added `stream_set_option()` + +### v3.4.1, 2019.10.19 + +- Fixed a [bug](https://github.com/opis/closure/issues/40) that prevented serialization to work correctly. + +### v3.4.0, 2019.09.03 + +- Added `createClosure` static method in `Opis\Closure\SerializableClosure`. +This method creates a new closure from arbitrary code, emulating `create_function`, +but without using eval + +### v3.3.1, 2019.07.10 + +- Use `sha1` instead of `md5` for hashing file names in `Opis\Closure\ReflectionClosure` class + +### v3.3.0, 2019.05.31 + +- Fixed a bug that prevented signed closures to properly work when the serialized string +contains invalid UTF-8 chars. Starting with this version `json_encode` is no longer used +when signing a closure. Backward compatibility is maintained and all closures that were +previously signed using the old method will continue to work. + +### v3.2.0, 2019.05.05 + +- Since an unsigned closure can be unserialized when no security provider is set, +there is no reason to treat differently a signed closure in the same situation. +Therefore, the `Opis\Closure\SecurityException` exception is no longer thrown when +unserializing a signed closure, if no security provider is set. + +### v3.1.6, 2019.02.22 + +- Fixed a bug that occurred when trying to set properties of classes that were not defined in user-land. +Those properties are now ignored. + ### v3.1.5, 2019.01.14 - Improved parser @@ -10,7 +56,7 @@ CHANGELOG - Added support for static methods that are named using PHP keywords or magic constants. Ex: `A::new()`, `A::use()`, `A::if()`, `A::function()`, `A::__DIR__()`, etc. - Used `@internal` to mark classes & methods that are for internal use only and -backward compatibility might be broken at some point. +backward compatibility is not guaranteed. ### v3.1.3, 2019.01.07 diff --git a/vendor/opis/closure/LICENSE b/vendor/opis/closure/LICENSE index 0d3d8dc..9c0a19b 100644 --- a/vendor/opis/closure/LICENSE +++ b/vendor/opis/closure/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2018 Zindex Software +Copyright (c) 2018-2019 Zindex Software Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/vendor/opis/closure/README.md b/vendor/opis/closure/README.md index 47d0434..b98d68d 100644 --- a/vendor/opis/closure/README.md +++ b/vendor/opis/closure/README.md @@ -61,7 +61,7 @@ Or you could directly reference it into your `composer.json` file as a dependenc ```json { "require": { - "opis/closure": "^3.1" + "opis/closure": "^3.5" } } ``` diff --git a/vendor/opis/closure/autoload.php b/vendor/opis/closure/autoload.php index 5c214b3..a928014 100644 --- a/vendor/opis/closure/autoload.php +++ b/vendor/opis/closure/autoload.php @@ -1,6 +1,6 @@ pointer >= $this->length; } + public function stream_set_option($option, $arg1, $arg2) + { + return false; + } + public function stream_stat() { $stat = stat(__FILE__); diff --git a/vendor/opis/closure/src/ISecurityProvider.php b/vendor/opis/closure/src/ISecurityProvider.php index 4e50761..d3b0a29 100644 --- a/vendor/opis/closure/src/ISecurityProvider.php +++ b/vendor/opis/closure/src/ISecurityProvider.php @@ -1,6 +1,6 @@ isStaticClosure; } + public function isShortClosure() + { + if ($this->isShortClosure === null) { + $code = $this->getCode(); + if ($this->isStatic()) { + $code = substr($code, 6); + } + $this->isShortClosure = strtolower(substr(trim($code), 0, 2)) === 'fn'; + } + + return $this->isShortClosure; + } + /** * @return string */ @@ -70,6 +84,7 @@ public function getCode() } $className = null; + $fn = false; if (null !== $className = $this->getClosureScopeClass()) { @@ -89,6 +104,7 @@ public function getCode() default: $php7_types = array('string', 'int', 'bool', 'float', 'void', 'object'); } + $fn = PHP_MINOR_VERSION === 4; } $ns = $this->getNamespaceName(); @@ -107,6 +123,7 @@ public function getCode() $tokens = $this->getTokens(); $state = $lastState = 'start'; $inside_anonymous = false; + $isShortClosure = false; $anonymous_mark = 0; $open = 0; $code = ''; @@ -124,6 +141,10 @@ public function getCode() if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) { $code .= $token[1]; $state = $token[0] === T_FUNCTION ? 'function' : 'static'; + } elseif ($fn && $token[0] === T_FN) { + $isShortClosure = true; + $code .= $token[1]; + $state = 'closure_args'; } break; case 'static': @@ -132,6 +153,10 @@ public function getCode() if ($token[0] === T_FUNCTION) { $state = 'function'; } + } elseif ($fn && $token[0] === T_FN) { + $isShortClosure = true; + $code .= $token[1]; + $state = 'closure_args'; } else { $code = ''; $state = 'start'; @@ -155,6 +180,10 @@ public function getCode() if($token[0] === T_FUNCTION || $token[0] === T_STATIC){ $code = $token[1]; $state = $token[0] === T_FUNCTION ? 'function' : 'static'; + } elseif ($fn && $token[0] === T_FN) { + $isShortClosure = true; + $code .= $token[1]; + $state = 'closure_args'; } break; case 'closure_args': @@ -172,6 +201,12 @@ public function getCode() $code .= $token[1]; $state = 'use'; break; + case T_DOUBLE_ARROW: + $code .= $token[1]; + if ($isShortClosure) { + $state = 'closure'; + } + break; case '=': $code .= $token; $lastState = 'closure_args'; @@ -226,6 +261,12 @@ public function getCode() $state = 'id_name'; $lastState = 'return'; break 2; + case T_DOUBLE_ARROW: + $code .= $token[1]; + if ($isShortClosure) { + $state = 'closure'; + } + break; case '{': $code .= '{'; $state = 'closure'; @@ -247,12 +288,36 @@ public function getCode() break; case '}': $code .= '}'; - if(--$open === 0){ + if(--$open === 0 && !$isShortClosure){ break 3; } elseif ($inside_anonymous) { $inside_anonymous = !($open === $anonymous_mark); } break; + case '(': + case '[': + $code .= $token[0]; + if ($isShortClosure) { + $open++; + } + break; + case ')': + case ']': + if ($isShortClosure) { + if ($open === 0) { + break 3; + } + --$open; + } + $code .= $token[0]; + break; + case ',': + case ';': + if ($isShortClosure && $open === 0) { + break 3; + } + $code .= $token[0]; + break; case T_LINE: $code .= $token[2] - $line + $lineAdd; break; @@ -432,6 +497,9 @@ public function getCode() $id_name .= $token[1]; break; case '(': + if ($isShortClosure) { + $open++; + } if($context === 'new' || false !== strpos($id_name, '\\')){ if($id_start !== '\\'){ if ($classes === null) { @@ -538,10 +606,17 @@ public function getCode() } } + if ($isShortClosure) { + $code .= ';'; + $this->useVariables = $this->getStaticVariables(); + } else { + $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use)); + } + + $this->isShortClosure = $isShortClosure; $this->isBindingRequired = $isUsingThisObject; $this->isScopeRequired = $isUsingScope; $this->code = $code; - $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use)); return $this->code; } @@ -615,7 +690,7 @@ public function isScopeRequired() protected function getHashedFileName() { if ($this->hashedName === null) { - $this->hashedName = md5($this->getFileName()); + $this->hashedName = sha1($this->getFileName()); } return $this->hashedName; diff --git a/vendor/opis/closure/src/SecurityException.php b/vendor/opis/closure/src/SecurityException.php index 7767a25..6a107ee 100644 --- a/vendor/opis/closure/src/SecurityException.php +++ b/vendor/opis/closure/src/SecurityException.php @@ -1,6 +1,6 @@ $this->reference, )); - if(static::$securityProvider !== null){ - $ret = '@' . json_encode(static::$securityProvider->sign($ret)); + if (static::$securityProvider !== null) { + $data = static::$securityProvider->sign($ret); + $ret = '@' . $data['hash'] . '.' . $data['closure']; } if (!--$this->scope->serializations && !--$this->scope->toserialize) { @@ -193,7 +194,20 @@ public function unserialize($data) "Make sure you use a security provider for both serialization and unserialization."); } - $data = json_decode(substr($data, 1), true); + if ($data[1] !== '{') { + $separator = strpos($data, '.'); + if ($separator === false) { + throw new SecurityException('Invalid signed closure'); + } + $hash = substr($data, 1, $separator - 1); + $closure = substr($data, $separator + 1); + + $data = ['hash' => $hash, 'closure' => $closure]; + + unset($hash, $closure); + } else { + $data = json_decode(substr($data, 1), true); + } if (!is_array($data) || !static::$securityProvider->verify($data)) { throw new SecurityException("Your serialized closure might have been modified and it's unsafe to be unserialized. " . @@ -203,8 +217,26 @@ public function unserialize($data) $data = $data['closure']; } elseif ($data[0] === '@') { - throw new SecurityException("The serialized closure is signed. ". - "Make sure you use a security provider for both serialization and unserialization."); + if ($data[1] !== '{') { + $separator = strpos($data, '.'); + if ($separator === false) { + throw new SecurityException('Invalid signed closure'); + } + $hash = substr($data, 1, $separator - 1); + $closure = substr($data, $separator + 1); + + $data = ['hash' => $hash, 'closure' => $closure]; + + unset($hash, $closure); + } else { + $data = json_decode(substr($data, 1), true); + } + + if (!is_array($data) || !isset($data['closure']) || !isset($data['hash'])) { + throw new SecurityException('Invalid signed closure'); + } + + $data = $data['closure']; } $this->code = \unserialize($data); @@ -338,8 +370,6 @@ public static function getSecurityProvider() */ public static function wrapClosures(&$data, SplObjectStorage $storage = null) { - static::enterContext(); - if($storage === null){ $storage = static::$context->scope; } @@ -387,7 +417,7 @@ public static function wrapClosures(&$data, SplObjectStorage $storage = null) break; } foreach ($reflection->getProperties() as $property){ - if($property->isStatic()){ + if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ continue; } $property->setAccessible(true); @@ -399,8 +429,6 @@ public static function wrapClosures(&$data, SplObjectStorage $storage = null) }; } while($reflection = $reflection->getParentClass()); } - - static::exitContext(); } /** @@ -450,7 +478,7 @@ public static function unwrapClosures(&$data, SplObjectStorage $storage = null) break; } foreach ($reflection->getProperties() as $property){ - if($property->isStatic()){ + if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ continue; } $property->setAccessible(true); @@ -464,6 +492,20 @@ public static function unwrapClosures(&$data, SplObjectStorage $storage = null) } } + /** + * Creates a new closure from arbitrary code, + * emulating create_function, but without using eval + * + * @param string$args + * @param string $code + * @return Closure + */ + public static function createClosure($args, $code) + { + ClosureStream::register(); + return include(ClosureStream::STREAM_PROTO . '://function(' . $args. '){' . $code . '};'); + } + /** * Internal method used to map closure pointers * @internal @@ -517,7 +559,7 @@ protected function mapPointers(&$data) break; } foreach ($reflection->getProperties() as $property){ - if($property->isStatic()){ + if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ continue; } $property->setAccessible(true); @@ -609,7 +651,7 @@ protected function mapByReference(&$data) break; } foreach ($reflection->getProperties() as $property){ - if($property->isStatic()){ + if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ continue; } $property->setAccessible(true); diff --git a/vendor/paragonie/random_compat/build-phar.sh b/vendor/paragonie/random_compat/build-phar.sh deleted file mode 100755 index b4a5ba3..0000000 --- a/vendor/paragonie/random_compat/build-phar.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) - -php -dphar.readonly=0 "$basedir/other/build_phar.php" $* \ No newline at end of file diff --git a/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json deleted file mode 100644 index 1fa8de9..0000000 --- a/vendor/paragonie/random_compat/composer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "paragonie/random_compat", - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "random", - "polyfill", - "pseudorandom" - ], - "license": "MIT", - "type": "library", - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "support": { - "issues": "https://github.com/paragonie/random_compat/issues", - "email": "info@paragonie.com", - "source": "https://github.com/paragonie/random_compat" - }, - "require": { - "php": "^7" - }, - "require-dev": { - "vimeo/psalm": "^1", - "phpunit/phpunit": "4.*|5.*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - } -} diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey deleted file mode 100644 index eb50ebf..0000000 --- a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PUBLIC KEY----- -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm -pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p -+h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc ------END PUBLIC KEY----- diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc deleted file mode 100644 index 6a1d7f3..0000000 --- a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v2.0.22 (MingW32) - -iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip -QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg -1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW -NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA -NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV -JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74= -=B6+8 ------END PGP SIGNATURE----- diff --git a/vendor/paragonie/random_compat/lib/random.php b/vendor/paragonie/random_compat/lib/random.php deleted file mode 100644 index c7731a5..0000000 --- a/vendor/paragonie/random_compat/lib/random.php +++ /dev/null @@ -1,32 +0,0 @@ -buildFromDirectory(dirname(__DIR__).'/lib'); -rename( - dirname(__DIR__).'/lib/index.php', - dirname(__DIR__).'/lib/random.php' -); - -/** - * If we pass an (optional) path to a private key as a second argument, we will - * sign the Phar with OpenSSL. - * - * If you leave this out, it will produce an unsigned .phar! - */ -if ($argc > 1) { - if (!@is_readable($argv[1])) { - echo 'Could not read the private key file:', $argv[1], "\n"; - exit(255); - } - $pkeyFile = file_get_contents($argv[1]); - - $private = openssl_get_privatekey($pkeyFile); - if ($private !== false) { - $pkey = ''; - openssl_pkey_export($private, $pkey); - $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey); - - /** - * Save the corresponding public key to the file - */ - if (!@is_readable($dist.'/random_compat.phar.pubkey')) { - $details = openssl_pkey_get_details($private); - file_put_contents( - $dist.'/random_compat.phar.pubkey', - $details['key'] - ); - } - } else { - echo 'An error occurred reading the private key from OpenSSL.', "\n"; - exit(255); - } -} diff --git a/vendor/paragonie/random_compat/psalm-autoload.php b/vendor/paragonie/random_compat/psalm-autoload.php deleted file mode 100644 index d71d1b8..0000000 --- a/vendor/paragonie/random_compat/psalm-autoload.php +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/vendor/react/cache/.travis.yml b/vendor/react/cache/.travis.yml index 290df75..402a996 100644 --- a/vendor/react/cache/.travis.yml +++ b/vendor/react/cache/.travis.yml @@ -5,8 +5,11 @@ php: - 5.4 - 5.5 - 5.6 - - 7 - - hhvm + - 7.0 + - 7.1 + - 7.2 + - 7.3 +# - hhvm # requires legacy phpunit & ignore errors, see below # lock distro so new future defaults will not break the build dist: trusty @@ -15,11 +18,15 @@ matrix: include: - php: 5.3 dist: precise + - php: hhvm + install: composer require phpunit/phpunit:^5 --dev --no-interaction + allow_failures: + - php: hhvm sudo: false install: - composer install --no-interaction - + script: - ./vendor/bin/phpunit --coverage-text diff --git a/vendor/react/cache/CHANGELOG.md b/vendor/react/cache/CHANGELOG.md index c2df5cc..99ecd1c 100644 --- a/vendor/react/cache/CHANGELOG.md +++ b/vendor/react/cache/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 1.0.0 (2019-07-11) + +* First stable LTS release, now following [SemVer](https://semver.org/). + We'd like to emphasize that this component is production ready and battle-tested. + We plan to support all long-term support (LTS) releases for at least 24 months, + so you have a rock-solid foundation to build on top of. + +> Contains no other changes, so it's actually fully compatible with the v0.6.0 release. + +## 0.6.0 (2019-07-04) + +* Feature / BC break: Add support for `getMultiple()`, `setMultiple()`, `deleteMultiple()`, `clear()` and `has()` + supporting multiple cache items (inspired by PSR-16). + (#32 by @krlv and #37 by @clue) + +* Documentation for TTL precision with millisecond accuracy or below and + use high-resolution timer for cache TTL on PHP 7.3+. + (#35 and #38 by @clue) + +* Improve API documentation and allow legacy HHVM to fail in Travis CI config. + (#34 and #36 by @clue) + +* Prefix all global functions calls with \ to skip the look up and resolve process and go straight to the global function. + (#31 by @WyriHaximus) + ## 0.5.0 (2018-06-25) * Improve documentation by describing what is expected of a class implementing `CacheInterface`. diff --git a/vendor/react/cache/README.md b/vendor/react/cache/README.md index 9e7ba2b..74cef54 100644 --- a/vendor/react/cache/README.md +++ b/vendor/react/cache/README.md @@ -1,6 +1,6 @@ -# Cache Component +# Cache -[![Build Status](https://secure.travis-ci.org/reactphp/cache.png?branch=master)](http://travis-ci.org/reactphp/cache) [![Code Climate](https://codeclimate.com/github/reactphp/cache/badges/gpa.svg)](https://codeclimate.com/github/reactphp/cache) +[![Build Status](https://travis-ci.org/reactphp/cache.svg?branch=master)](https://travis-ci.org/reactphp/cache) Async, [Promise](https://github.com/reactphp/promise)-based cache interface for [ReactPHP](https://reactphp.org/). @@ -11,6 +11,9 @@ The cache component provides a implementation of that. This allows consumers to type hint against the interface and third parties to provide alternate implementations. +This project is heavily inspired by +[PSR-16: Common Interface for Caching Libraries](https://www.php-fig.org/psr/psr-16/), +but uses an interface more suited for async, non-blocking applications. **Table of Contents** @@ -19,6 +22,11 @@ provide alternate implementations. * [get()](#get) * [set()](#set) * [delete()](#delete) + * [getMultiple()](#getmultiple) + * [setMultiple()](#setmultiple) + * [deleteMultiple()](#deletemultiple) + * [clear()](#clear) + * [has()](#has) * [ArrayCache](#arraycache) * [Common usage](#common-usage) * [Fallback get](#fallback-get) @@ -37,7 +45,7 @@ provide alternate implementations. #### get() -The `get(string $key, mixed $default = null): PromiseInterface` method can be used to +The `get(string $key, mixed $default = null): PromiseInterface` method can be used to retrieve an item from the cache. This method will resolve with the cached value on success or with the @@ -57,7 +65,7 @@ This example fetches the value of the key `foo` and passes it to the #### set() -The `set(string $key, mixed $value, ?float $ttl = null): PromiseInterface` method can be used to +The `set(string $key, mixed $value, ?float $ttl = null): PromiseInterface` method can be used to store an item in the cache. This method will resolve with `true` on success or `false` when an error @@ -77,9 +85,29 @@ $cache->set('foo', 'bar', 60); This example eventually sets the value of the key `foo` to `bar`. If it already exists, it is overridden. +This interface does not enforce any particular TTL resolution, so special +care may have to be taken if you rely on very high precision with +millisecond accuracy or below. Cache implementations SHOULD work on a +best effort basis and SHOULD provide at least second accuracy unless +otherwise noted. Many existing cache implementations are known to provide +microsecond or millisecond accuracy, but it's generally not recommended +to rely on this high precision. + +This interface suggests that cache implementations SHOULD use a monotonic +time source if available. Given that a monotonic time source is only +available as of PHP 7.3 by default, cache implementations MAY fall back +to using wall-clock time. +While this does not affect many common use cases, this is an important +distinction for programs that rely on a high time precision or on systems +that are subject to discontinuous time adjustments (time jumps). +This means that if you store a cache item with a TTL of 30s and then +adjust your system time forward by 20s, the cache item SHOULD still +expire in 30s. + #### delete() -Deletes an item from the cache. +The `delete(string $key): PromiseInterface` method can be used to +delete an item from the cache. This method will resolve with `true` on success or `false` when an error occurs. When no item for `$key` is found in the cache, it also resolves @@ -94,6 +122,110 @@ This example eventually deletes the key `foo` from the cache. As with `set()`, this may not happen instantly and a promise is returned to provide guarantees whether or not the item has been removed from cache. +#### getMultiple() + +The `getMultiple(string[] $keys, mixed $default = null): PromiseInterface` method can be used to +retrieve multiple cache items by their unique keys. + +This method will resolve with an array of cached values on success or with the +given `$default` value when an item can not be found or when an error occurs. +Similarly, an expired cache item (once the time-to-live is expired) is +considered a cache miss. + +```php +$cache->getMultiple(array('name', 'age'))->then(function (array $values) { + $name = $values['name'] ?? 'User'; + $age = $values['age'] ?? 'n/a'; + + echo $name . ' is ' . $age . PHP_EOL; +}); +``` + +This example fetches the cache items for the `name` and `age` keys and +prints some example output. You can use any of the composition provided +by [promises](https://github.com/reactphp/promise). + +#### setMultiple() + +The `setMultiple(array $values, ?float $ttl = null): PromiseInterface` method can be used to +persist a set of key => value pairs in the cache, with an optional TTL. + +This method will resolve with `true` on success or `false` when an error +occurs. If the cache implementation has to go over the network to store +it, it may take a while. + +The optional `$ttl` parameter sets the maximum time-to-live in seconds +for these cache items. If this parameter is omitted (or `null`), these items +will stay in the cache for as long as the underlying implementation +supports. Trying to access an expired cache items results in a cache miss, +see also [`getMultiple()`](#getmultiple). + +```php +$cache->setMultiple(array('foo' => 1, 'bar' => 2), 60); +``` + +This example eventually sets the list of values - the key `foo` to `1` value +and the key `bar` to `2`. If some of the keys already exist, they are overridden. + +#### deleteMultiple() + +The `setMultiple(string[] $keys): PromiseInterface` method can be used to +delete multiple cache items in a single operation. + +This method will resolve with `true` on success or `false` when an error +occurs. When no items for `$keys` are found in the cache, it also resolves +to `true`. If the cache implementation has to go over the network to +delete it, it may take a while. + +```php +$cache->deleteMultiple(array('foo', 'bar, 'baz')); +``` + +This example eventually deletes keys `foo`, `bar` and `baz` from the cache. +As with `setMultiple()`, this may not happen instantly and a promise is returned to +provide guarantees whether or not the item has been removed from cache. + +#### clear() + +The `clear(): PromiseInterface` method can be used to +wipe clean the entire cache. + +This method will resolve with `true` on success or `false` when an error +occurs. If the cache implementation has to go over the network to +delete it, it may take a while. + +```php +$cache->clear(); +``` + +This example eventually deletes all keys from the cache. As with `deleteMultiple()`, +this may not happen instantly and a promise is returned to provide guarantees +whether or not all the items have been removed from cache. + +#### has() + +The `has(string $key): PromiseInterface` method can be used to +determine whether an item is present in the cache. + +This method will resolve with `true` on success or `false` when no item can be found +or when an error occurs. Similarly, an expired cache item (once the time-to-live +is expired) is considered a cache miss. + +```php +$cache + ->has('foo') + ->then('var_dump'); +``` + +This example checks if the value of the key `foo` is set in the cache and passes +the result to the `var_dump` function. You can use any of the composition provided by +[promises](https://github.com/reactphp/promise). + +NOTE: It is recommended that has() is only to be used for cache warming type purposes +and not to be used within your live applications operations for get/set, as this method +is subject to a race condition where your has() will return true and immediately after, +another script can remove it making the state of your app out of date. + ### ArrayCache The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface). @@ -120,6 +252,16 @@ $cache->set('bar', '2'); $cache->set('baz', '3'); ``` +This cache implementation is known to rely on wall-clock time to schedule +future cache expiration times when using any version before PHP 7.3, +because a monotonic time source is only available as of PHP 7.3 (`hrtime()`). +While this does not affect many common use cases, this is an important +distinction for programs that rely on a high time precision or on systems +that are subject to discontinuous time adjustments (time jumps). +This means that if you store a cache item with a TTL of 30s on PHP < 7.3 +and then adjust your system time forward by 20s, the cache item may +expire in 10s. See also [`set()`](#set) for more details. + ## Common usage ### Fallback get @@ -190,10 +332,11 @@ fetched from the database. The recommended way to install this library is [through Composer](https://getcomposer.org). [New to Composer?](https://getcomposer.org/doc/00-intro.md) +This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require react/cache:^0.5.0 +$ composer require react/cache:^1.0 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. diff --git a/vendor/react/cache/src/ArrayCache.php b/vendor/react/cache/src/ArrayCache.php index 314814a..81f25ef 100644 --- a/vendor/react/cache/src/ArrayCache.php +++ b/vendor/react/cache/src/ArrayCache.php @@ -3,12 +3,14 @@ namespace React\Cache; use React\Promise; +use React\Promise\PromiseInterface; class ArrayCache implements CacheInterface { private $limit; private $data = array(); private $expires = array(); + private $supportsHighResolution; /** * The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface). @@ -35,21 +37,34 @@ class ArrayCache implements CacheInterface * $cache->set('baz', '3'); * ``` * + * This cache implementation is known to rely on wall-clock time to schedule + * future cache expiration times when using any version before PHP 7.3, + * because a monotonic time source is only available as of PHP 7.3 (`hrtime()`). + * While this does not affect many common use cases, this is an important + * distinction for programs that rely on a high time precision or on systems + * that are subject to discontinuous time adjustments (time jumps). + * This means that if you store a cache item with a TTL of 30s on PHP < 7.3 + * and then adjust your system time forward by 20s, the cache item may + * expire in 10s. See also [`set()`](#set) for more details. + * * @param int|null $limit maximum number of entries to store in the LRU cache */ public function __construct($limit = null) { $this->limit = $limit; + + // prefer high-resolution timer, available as of PHP 7.3+ + $this->supportsHighResolution = \function_exists('hrtime'); } public function get($key, $default = null) { // delete key if it is already expired => below will detect this as a cache miss - if (isset($this->expires[$key]) && $this->expires[$key] < microtime(true)) { + if (isset($this->expires[$key]) && $this->now() - $this->expires[$key] > 0) { unset($this->data[$key], $this->expires[$key]); } - if (!array_key_exists($key, $this->data)) { + if (!\array_key_exists($key, $this->data)) { return Promise\resolve($default); } @@ -70,22 +85,22 @@ public function set($key, $value, $ttl = null) // sort expiration times if TTL is given (first will expire first) unset($this->expires[$key]); if ($ttl !== null) { - $this->expires[$key] = microtime(true) + $ttl; - asort($this->expires); + $this->expires[$key] = $this->now() + $ttl; + \asort($this->expires); } // ensure size limit is not exceeded or remove first entry from array - if ($this->limit !== null && count($this->data) > $this->limit) { + if ($this->limit !== null && \count($this->data) > $this->limit) { // first try to check if there's any expired entry // expiration times are sorted, so we can simply look at the first one - reset($this->expires); - $key = key($this->expires); + \reset($this->expires); + $key = \key($this->expires); // check to see if the first in the list of expiring keys is already expired // if the first key is not expired, we have to overwrite by using LRU info - if ($key === null || $this->expires[$key] > microtime(true)) { - reset($this->data); - $key = key($this->data); + if ($key === null || $this->now() - $this->expires[$key] < 0) { + \reset($this->data); + $key = \key($this->data); } unset($this->data[$key], $this->expires[$key]); } @@ -99,4 +114,68 @@ public function delete($key) return Promise\resolve(true); } + + public function getMultiple(array $keys, $default = null) + { + $values = array(); + + foreach ($keys as $key) { + $values[$key] = $this->get($key, $default); + } + + return Promise\all($values); + } + + public function setMultiple(array $values, $ttl = null) + { + foreach ($values as $key => $value) { + $this->set($key, $value, $ttl); + } + + return Promise\resolve(true); + } + + public function deleteMultiple(array $keys) + { + foreach ($keys as $key) { + unset($this->data[$key], $this->expires[$key]); + } + + return Promise\resolve(true); + } + + public function clear() + { + $this->data = array(); + $this->expires = array(); + + return Promise\resolve(true); + } + + public function has($key) + { + // delete key if it is already expired + if (isset($this->expires[$key]) && $this->now() - $this->expires[$key] > 0) { + unset($this->data[$key], $this->expires[$key]); + } + + if (!\array_key_exists($key, $this->data)) { + return Promise\resolve(false); + } + + // remove and append to end of array to keep track of LRU info + $value = $this->data[$key]; + unset($this->data[$key]); + $this->data[$key] = $value; + + return Promise\resolve(true); + } + + /** + * @return float + */ + private function now() + { + return $this->supportsHighResolution ? \hrtime(true) * 1e-9 : \microtime(true); + } } diff --git a/vendor/react/cache/src/CacheInterface.php b/vendor/react/cache/src/CacheInterface.php index f31a68e..3d52501 100644 --- a/vendor/react/cache/src/CacheInterface.php +++ b/vendor/react/cache/src/CacheInterface.php @@ -50,6 +50,25 @@ public function get($key, $default = null); * This example eventually sets the value of the key `foo` to `bar`. If it * already exists, it is overridden. * + * This interface does not enforce any particular TTL resolution, so special + * care may have to be taken if you rely on very high precision with + * millisecond accuracy or below. Cache implementations SHOULD work on a + * best effort basis and SHOULD provide at least second accuracy unless + * otherwise noted. Many existing cache implementations are known to provide + * microsecond or millisecond accuracy, but it's generally not recommended + * to rely on this high precision. + * + * This interface suggests that cache implementations SHOULD use a monotonic + * time source if available. Given that a monotonic time source is only + * available as of PHP 7.3 by default, cache implementations MAY fall back + * to using wall-clock time. + * While this does not affect many common use cases, this is an important + * distinction for programs that rely on a high time precision or on systems + * that are subject to discontinuous time adjustments (time jumps). + * This means that if you store a cache item with a TTL of 30s and then + * adjust your system time forward by 20s, the cache item SHOULD still + * expire in 30s. + * * @param string $key * @param mixed $value * @param ?float $ttl @@ -77,4 +96,99 @@ public function set($key, $value, $ttl = null); * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error */ public function delete($key); + + /** + * Retrieves multiple cache items by their unique keys. + * + * This method will resolve with an array of cached values on success or with the + * given `$default` value when an item can not be found or when an error occurs. + * Similarly, an expired cache item (once the time-to-live is expired) is + * considered a cache miss. + * + * ```php + * $cache->getMultiple(array('name', 'age'))->then(function (array $values) { + * $name = $values['name'] ?? 'User'; + * $age = $values['age'] ?? 'n/a'; + * + * echo $name . ' is ' . $age . PHP_EOL; + * }); + * ``` + * + * This example fetches the cache items for the `name` and `age` keys and + * prints some example output. You can use any of the composition provided + * by [promises](https://github.com/reactphp/promise). + * + * @param string[] $keys A list of keys that can obtained in a single operation. + * @param mixed $default Default value to return for keys that do not exist. + * @return PromiseInterface Returns a promise which resolves to an `array` of cached values + */ + public function getMultiple(array $keys, $default = null); + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * This method will resolve with `true` on success or `false` when an error + * occurs. If the cache implementation has to go over the network to store + * it, it may take a while. + * + * The optional `$ttl` parameter sets the maximum time-to-live in seconds + * for these cache items. If this parameter is omitted (or `null`), these items + * will stay in the cache for as long as the underlying implementation + * supports. Trying to access an expired cache items results in a cache miss, + * see also [`get()`](#get). + * + * ```php + * $cache->setMultiple(array('foo' => 1, 'bar' => 2), 60); + * ``` + * + * This example eventually sets the list of values - the key `foo` to 1 value + * and the key `bar` to 2. If some of the keys already exist, they are overridden. + * + * @param array $values A list of key => value pairs for a multiple-set operation. + * @param ?float $ttl Optional. The TTL value of this item. + * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error + */ + public function setMultiple(array $values, $ttl = null); + + /** + * Deletes multiple cache items in a single operation. + * + * @param string[] $keys A list of string-based keys to be deleted. + * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error + */ + public function deleteMultiple(array $keys); + + /** + * Wipes clean the entire cache. + * + * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error + */ + public function clear(); + + /** + * Determines whether an item is present in the cache. + * + * This method will resolve with `true` on success or `false` when no item can be found + * or when an error occurs. Similarly, an expired cache item (once the time-to-live + * is expired) is considered a cache miss. + * + * ```php + * $cache + * ->has('foo') + * ->then('var_dump'); + * ``` + * + * This example checks if the value of the key `foo` is set in the cache and passes + * the result to the `var_dump` function. You can use any of the composition provided by + * [promises](https://github.com/reactphp/promise). + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error + */ + public function has($key); } diff --git a/vendor/react/cache/tests/ArrayCacheTest.php b/vendor/react/cache/tests/ArrayCacheTest.php index 3336012..3b5bd8c 100644 --- a/vendor/react/cache/tests/ArrayCacheTest.php +++ b/vendor/react/cache/tests/ArrayCacheTest.php @@ -193,4 +193,130 @@ public function testSetWillOverwriteExpiredItemIfAnyEntryIsExpired() $this->cache->get('foo')->then($this->expectCallableOnceWith('1')); $this->cache->get('bar')->then($this->expectCallableOnceWith(null)); } + + public function testGetMultiple() + { + $this->cache = new ArrayCache(); + $this->cache->set('foo', '1'); + + $this->cache + ->getMultiple(array('foo', 'bar'), 'baz') + ->then($this->expectCallableOnceWith(array('foo' => '1', 'bar' => 'baz'))); + } + + public function testSetMultiple() + { + $this->cache = new ArrayCache(); + $this->cache->setMultiple(array('foo' => '1', 'bar' => '2'), 10); + + $this->cache + ->getMultiple(array('foo', 'bar')) + ->then($this->expectCallableOnceWith(array('foo' => '1', 'bar' => '2'))); + } + + public function testDeleteMultiple() + { + $this->cache = new ArrayCache(); + $this->cache->setMultiple(array('foo' => 1, 'bar' => 2, 'baz' => 3)); + + $this->cache + ->deleteMultiple(array('foo', 'baz')) + ->then($this->expectCallableOnceWith(true)); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(false)); + + $this->cache + ->has('bar') + ->then($this->expectCallableOnceWith(true)); + + $this->cache + ->has('baz') + ->then($this->expectCallableOnceWith(false)); + } + + public function testClearShouldClearCache() + { + $this->cache = new ArrayCache(); + $this->cache->setMultiple(array('foo' => 1, 'bar' => 2, 'baz' => 3)); + + $this->cache->clear(); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(false)); + + $this->cache + ->has('bar') + ->then($this->expectCallableOnceWith(false)); + + $this->cache + ->has('baz') + ->then($this->expectCallableOnceWith(false)); + } + + public function hasShouldResolvePromiseForExistingKey() + { + $this->cache = new ArrayCache(); + $this->cache->set('foo', 'bar'); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(true)); + } + + public function hasShouldResolvePromiseForNonExistentKey() + { + $this->cache = new ArrayCache(); + $this->cache->set('foo', 'bar'); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(false)); + } + + public function testHasWillResolveIfItemIsNotExpired() + { + $this->cache = new ArrayCache(); + $this->cache->set('foo', '1', 10); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(true)); + } + + public function testHasWillResolveIfItemIsExpired() + { + $this->cache = new ArrayCache(); + $this->cache->set('foo', '1', 0); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(false)); + } + + public function testHasWillResolveForExplicitNullValue() + { + $this->cache = new ArrayCache(); + $this->cache->set('foo', null); + + $this->cache + ->has('foo') + ->then($this->expectCallableOnceWith(true)); + } + + public function testHasWithLimitedSizeWillUpdateLRUInfo() + { + $this->cache = new ArrayCache(2); + + $this->cache->set('foo', 1); + $this->cache->set('bar', 2); + $this->cache->has('foo')->then($this->expectCallableOnceWith(true)); + $this->cache->set('baz', 3); + + $this->cache->has('foo')->then($this->expectCallableOnceWith(1)); + $this->cache->has('bar')->then($this->expectCallableOnceWith(false)); + $this->cache->has('baz')->then($this->expectCallableOnceWith(3)); + } } diff --git a/vendor/react/dns/.travis.yml b/vendor/react/dns/.travis.yml index 41921e3..459e852 100644 --- a/vendor/react/dns/.travis.yml +++ b/vendor/react/dns/.travis.yml @@ -8,7 +8,8 @@ php: - 7.0 - 7.1 - 7.2 - - hhvm # ignore errors, see below + - 7.3 +# - hhvm # requires legacy phpunit & ignore errors, see below # lock distro so new future defaults will not break the build dist: trusty @@ -17,6 +18,8 @@ matrix: include: - php: 5.3 dist: precise + - php: hhvm + install: composer require phpunit/phpunit:^5 --dev --no-interaction allow_failures: - php: hhvm diff --git a/vendor/react/dns/CHANGELOG.md b/vendor/react/dns/CHANGELOG.md index 8c06569..4f651a5 100644 --- a/vendor/react/dns/CHANGELOG.md +++ b/vendor/react/dns/CHANGELOG.md @@ -1,5 +1,90 @@ # Changelog +## 1.2.0 (2019-08-15) + +* Feature: Add `TcpTransportExecutor` to send DNS queries over TCP/IP connection, + add `SelectiveTransportExecutor` to retry with TCP if UDP is truncated and + automatically select transport protocol when no explicit `udp://` or `tcp://` scheme is given in `Factory`. + (#145, #146, #147 and #148 by @clue) + +* Feature: Support escaping literal dots and special characters in domain names. + (#144 by @clue) + +## 1.1.0 (2019-07-18) + +* Feature: Support parsing `CAA` and `SSHFP` records. + (#141 and #142 by @clue) + +* Feature: Add `ResolverInterface` as common interface for `Resolver` class. + (#139 by @clue) + +* Fix: Add missing private property definitions and + remove unneeded dependency on `react/stream`. + (#140 and #143 by @clue) + +## 1.0.0 (2019-07-11) + +* First stable LTS release, now following [SemVer](https://semver.org/). + We'd like to emphasize that this component is production ready and battle-tested. + We plan to support all long-term support (LTS) releases for at least 24 months, + so you have a rock-solid foundation to build on top of. + +This update involves a number of BC breaks due to dropped support for +deprecated functionality and some internal API cleanup. We've tried hard to +avoid BC breaks where possible and minimize impact otherwise. We expect that +most consumers of this package will actually not be affected by any BC +breaks, see below for more details: + +* BC break: Delete all deprecated APIs, use `Query` objects for `Message` questions + instead of nested arrays and increase code coverage to 100%. + (#130 by @clue) + +* BC break: Move `$nameserver` from `ExecutorInterface` to `UdpTransportExecutor`, + remove advanced/internal `UdpTransportExecutor` args for `Parser`/`BinaryDumper` and + add API documentation for `ExecutorInterface`. + (#135, #137 and #138 by @clue) + +* BC break: Replace `HeaderBag` attributes with simple `Message` properties. + (#132 by @clue) + +* BC break: Mark all `Record` attributes as required, add documentation vs `Query`. + (#136 by @clue) + +* BC break: Mark all classes as final to discourage inheritance + (#134 by @WyriHaximus) + +## 0.4.19 (2019-07-10) + +* Feature: Avoid garbage references when DNS resolution rejects on legacy PHP <= 5.6. + (#133 by @clue) + +## 0.4.18 (2019-09-07) + +* Feature / Fix: Implement `CachingExecutor` using cache TTL, deprecate old `CachedExecutor`, + respect TTL from response records when caching and do not cache truncated responses. + (#129 by @clue) + +* Feature: Limit cache size to 256 last responses by default. + (#127 by @clue) + +* Feature: Cooperatively resolve hosts to avoid running same query concurrently. + (#125 by @clue) + +## 0.4.17 (2019-04-01) + +* Feature: Support parsing `authority` and `additional` records from DNS response. + (#123 by @clue) + +* Feature: Support dumping records as part of outgoing binary DNS message. + (#124 by @clue) + +* Feature: Forward compatibility with upcoming Cache v0.6 and Cache v1.0 + (#121 by @clue) + +* Improve test suite to add forward compatibility with PHPUnit 7, + test against PHP 7.3 and use legacy PHPUnit 5 on legacy HHVM. + (#122 by @clue) + ## 0.4.16 (2018-11-11) * Feature: Improve promise cancellation for DNS lookup retries and clean up any garbage references. diff --git a/vendor/react/dns/README.md b/vendor/react/dns/README.md index 043b937..fecbd11 100644 --- a/vendor/react/dns/README.md +++ b/vendor/react/dns/README.md @@ -13,11 +13,13 @@ easily be used to create a DNS server. * [Basic usage](#basic-usage) * [Caching](#caching) * [Custom cache adapter](#custom-cache-adapter) -* [Resolver](#resolver) +* [ResolverInterface](#resolverinterface) * [resolve()](#resolve) * [resolveAll()](#resolveall) * [Advanced usage](#advanced-usage) * [UdpTransportExecutor](#udptransportexecutor) + * [TcpTransportExecutor](#tcptransportexecutor) + * [SelectiveTransportExecutor](#selectivetransportexecutor) * [HostsFileExecutor](#hostsfileexecutor) * [Install](#install) * [Tests](#tests) @@ -111,7 +113,9 @@ $dns = $factory->createCached('8.8.8.8', $loop, $cache); See also the wiki for possible [cache implementations](https://github.com/reactphp/react/wiki/Users#cache-implementations). -## Resolver +## ResolverInterface + + ### resolve() @@ -208,10 +212,9 @@ The following example looks up the `IPv6` address for `igor.io`. ```php $loop = Factory::create(); -$executor = new UdpTransportExecutor($loop); +$executor = new UdpTransportExecutor('8.8.8.8:53', $loop); $executor->query( - '8.8.8.8:53', new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) )->then(function (Message $message) { foreach ($message->answers as $answer) { @@ -229,7 +232,7 @@ want to use this in combination with a `TimeoutExecutor` like this: ```php $executor = new TimeoutExecutor( - new UdpTransportExecutor($loop), + new UdpTransportExecutor($nameserver, $loop), 3.0, $loop ); @@ -242,19 +245,160 @@ combination with a `RetryExecutor` like this: ```php $executor = new RetryExecutor( new TimeoutExecutor( - new UdpTransportExecutor($loop), + new UdpTransportExecutor($nameserver, $loop), 3.0, $loop ) ); ``` +Note that this executor is entirely async and as such allows you to execute +any number of queries concurrently. You should probably limit the number of +concurrent queries in your application or you're very likely going to face +rate limitations and bans on the resolver end. For many common applications, +you may want to avoid sending the same query multiple times when the first +one is still pending, so you will likely want to use this in combination with +a `CoopExecutor` like this: + +```php +$executor = new CoopExecutor( + new RetryExecutor( + new TimeoutExecutor( + new UdpTransportExecutor($nameserver, $loop), + 3.0, + $loop + ) + ) +); +``` + > Internally, this class uses PHP's UDP sockets and does not take advantage of [react/datagram](https://github.com/reactphp/datagram) purely for organizational reasons to avoid a cyclic dependency between the two packages. Higher-level components should take advantage of the Datagram component instead of reimplementing this socket logic from scratch. +### TcpTransportExecutor + +The `TcpTransportExecutor` class can be used to +send DNS queries over a TCP/IP stream transport. + +This is one of the main classes that send a DNS query to your DNS server. + +For more advanced usages one can utilize this class directly. +The following example looks up the `IPv6` address for `reactphp.org`. + +```php +$loop = Factory::create(); +$executor = new TcpTransportExecutor('8.8.8.8:53', $loop); + +$executor->query( + new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) +)->then(function (Message $message) { + foreach ($message->answers as $answer) { + echo 'IPv6: ' . $answer->data . PHP_EOL; + } +}, 'printf'); + +$loop->run(); +``` + +See also [example #92](examples). + +Note that this executor does not implement a timeout, so you will very likely +want to use this in combination with a `TimeoutExecutor` like this: + +```php +$executor = new TimeoutExecutor( + new TcpTransportExecutor($nameserver, $loop), + 3.0, + $loop +); +``` + +Unlike the `UdpTransportExecutor`, this class uses a reliable TCP/IP +transport, so you do not necessarily have to implement any retry logic. + +Note that this executor is entirely async and as such allows you to execute +queries concurrently. The first query will establish a TCP/IP socket +connection to the DNS server which will be kept open for a short period. +Additional queries will automatically reuse this existing socket connection +to the DNS server, will pipeline multiple requests over this single +connection and will keep an idle connection open for a short period. The +initial TCP/IP connection overhead may incur a slight delay if you only send +occasional queries – when sending a larger number of concurrent queries over +an existing connection, it becomes increasingly more efficient and avoids +creating many concurrent sockets like the UDP-based executor. You may still +want to limit the number of (concurrent) queries in your application or you +may be facing rate limitations and bans on the resolver end. For many common +applications, you may want to avoid sending the same query multiple times +when the first one is still pending, so you will likely want to use this in +combination with a `CoopExecutor` like this: + +```php +$executor = new CoopExecutor( + new TimeoutExecutor( + new TcpTransportExecutor($nameserver, $loop), + 3.0, + $loop + ) +); +``` + +> Internally, this class uses PHP's TCP/IP sockets and does not take advantage + of [react/socket](https://github.com/reactphp/socket) purely for + organizational reasons to avoid a cyclic dependency between the two + packages. Higher-level components should take advantage of the Socket + component instead of reimplementing this socket logic from scratch. + +### SelectiveTransportExecutor + +The `SelectiveTransportExecutor` class can be used to +Send DNS queries over a UDP or TCP/IP stream transport. + +This class will automatically choose the correct transport protocol to send +a DNS query to your DNS server. It will always try to send it over the more +efficient UDP transport first. If this query yields a size related issue +(truncated messages), it will retry over a streaming TCP/IP transport. + +For more advanced usages one can utilize this class directly. +The following example looks up the `IPv6` address for `reactphp.org`. + +```php +$executor = new SelectiveTransportExecutor($udpExecutor, $tcpExecutor); + +$executor->query( + new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) +)->then(function (Message $message) { + foreach ($message->answers as $answer) { + echo 'IPv6: ' . $answer->data . PHP_EOL; + } +}, 'printf'); +``` + +Note that this executor only implements the logic to select the correct +transport for the given DNS query. Implementing the correct transport logic, +implementing timeouts and any retry logic is left up to the given executors, +see also [`UdpTransportExecutor`](#udptransportexecutor) and +[`TcpTransportExecutor`](#tcptransportexecutor) for more details. + +Note that this executor is entirely async and as such allows you to execute +any number of queries concurrently. You should probably limit the number of +concurrent queries in your application or you're very likely going to face +rate limitations and bans on the resolver end. For many common applications, +you may want to avoid sending the same query multiple times when the first +one is still pending, so you will likely want to use this in combination with +a `CoopExecutor` like this: + +```php +$executor = new CoopExecutor( + new SelectiveTransportExecutor( + $datagramExecutor, + $streamExecutor + ) +); +``` + ### HostsFileExecutor Note that the above `UdpTransportExecutor` class always performs an actual DNS query. @@ -264,11 +408,10 @@ use this code: ```php $hosts = \React\Dns\Config\HostsFile::loadFromPathBlocking(); -$executor = new UdpTransportExecutor($loop); +$executor = new UdpTransportExecutor('8.8.8.8:53', $loop); $executor = new HostsFileExecutor($hosts, $executor); $executor->query( - '8.8.8.8:53', new Query('localhost', Message::TYPE_A, Message::CLASS_IN) ); ``` @@ -278,10 +421,11 @@ $executor->query( The recommended way to install this library is [through Composer](https://getcomposer.org). [New to Composer?](https://getcomposer.org/doc/00-intro.md) +This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require react/dns:^0.4.16 +$ composer require react/dns:^1.2 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. diff --git a/vendor/react/dns/composer.json b/vendor/react/dns/composer.json index 40010c2..5c0e47a 100644 --- a/vendor/react/dns/composer.json +++ b/vendor/react/dns/composer.json @@ -5,15 +5,14 @@ "license": "MIT", "require": { "php": ">=5.3.0", - "react/cache": "^0.5 || ^0.4 || ^0.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.1 || ^1.2.1", - "react/promise-timer": "^1.2", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.0 || ^0.5", + "react/promise": "^2.7 || ^1.2.1", + "react/promise-timer": "^1.2" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "autoload": { "psr-4": { "React\\Dns\\": "src" } diff --git a/vendor/react/dns/examples/12-all-types.php b/vendor/react/dns/examples/12-all-types.php index 438ee86..adffa00 100644 --- a/vendor/react/dns/examples/12-all-types.php +++ b/vendor/react/dns/examples/12-all-types.php @@ -1,5 +1,8 @@ query('8.8.8.8:53', $ipv4Query)->then(function (Message $message) { +$executor->query($ipv4Query)->then(function (Message $message) { foreach ($message->answers as $answer) { echo 'IPv4: ' . $answer->data . PHP_EOL; } }, 'printf'); -$executor->query('8.8.8.8:53', $ipv6Query)->then(function (Message $message) { +$executor->query($ipv6Query)->then(function (Message $message) { foreach ($message->answers as $answer) { echo 'IPv6: ' . $answer->data . PHP_EOL; } diff --git a/vendor/react/dns/examples/92-query-any.php b/vendor/react/dns/examples/92-query-any.php index dcc14ae..3b98fe2 100644 --- a/vendor/react/dns/examples/92-query-any.php +++ b/vendor/react/dns/examples/92-query-any.php @@ -1,21 +1,24 @@ query('8.8.8.8:53', $any)->then(function (Message $message) { +$executor->query($any)->then(function (Message $message) { foreach ($message->answers as $answer) { /* @var $answer Record */ @@ -49,15 +52,25 @@ $data = implode(' ', $data); break; case Message::TYPE_SRV: - // SRV records contains priority, weight, port and target, dump structure here + // SRV records contain priority, weight, port and target, dump structure here $type = 'SRV'; $data = json_encode($data); break; + case Message::TYPE_SSHFP: + // SSHFP records contain algorithm, fingerprint type and hex fingerprint value + $type = 'SSHFP'; + $data = implode(' ', $data); + break; case Message::TYPE_SOA: // SOA records contain structured data, dump structure here $type = 'SOA'; $data = json_encode($data); break; + case Message::TYPE_CAA: + // CAA records contains flag, tag and value + $type = 'CAA'; + $data = $data['flag'] . ' ' . $data['tag'] . ' "' . $data['value'] . '"'; + break; default: // unknown type uses HEX format $type = 'Type ' . $answer->type; diff --git a/vendor/react/dns/phpunit.xml.dist b/vendor/react/dns/phpunit.xml.dist index 13d3fab..04d426b 100644 --- a/vendor/react/dns/phpunit.xml.dist +++ b/vendor/react/dns/phpunit.xml.dist @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="vendor/autoload.php" > diff --git a/vendor/react/dns/src/BadServerException.php b/vendor/react/dns/src/BadServerException.php index 3bf50f1..3d95213 100644 --- a/vendor/react/dns/src/BadServerException.php +++ b/vendor/react/dns/src/BadServerException.php @@ -2,6 +2,6 @@ namespace React\Dns; -class BadServerException extends \Exception +final class BadServerException extends \Exception { } diff --git a/vendor/react/dns/src/Config/Config.php b/vendor/react/dns/src/Config/Config.php index c82635d..37ae91d 100644 --- a/vendor/react/dns/src/Config/Config.php +++ b/vendor/react/dns/src/Config/Config.php @@ -4,7 +4,7 @@ use RuntimeException; -class Config +final class Config { /** * Loads the system DNS configuration diff --git a/vendor/react/dns/src/Config/FilesystemFactory.php b/vendor/react/dns/src/Config/FilesystemFactory.php deleted file mode 100644 index 68cec3e..0000000 --- a/vendor/react/dns/src/Config/FilesystemFactory.php +++ /dev/null @@ -1,73 +0,0 @@ -loop = $loop; - } - - public function create($filename) - { - return $this - ->loadEtcResolvConf($filename) - ->then(array($this, 'parseEtcResolvConf')); - } - - /** - * @param string $contents - * @return Promise - * @deprecated see Config instead - */ - public function parseEtcResolvConf($contents) - { - return Promise\resolve(Config::loadResolvConfBlocking( - 'data://text/plain;base64,' . base64_encode($contents) - )); - } - - public function loadEtcResolvConf($filename) - { - if (!file_exists($filename)) { - return Promise\reject(new \InvalidArgumentException("The filename for /etc/resolv.conf given does not exist: $filename")); - } - - try { - $deferred = new Deferred(); - - $fd = fopen($filename, 'r'); - stream_set_blocking($fd, 0); - - $contents = ''; - - $stream = class_exists('React\Stream\ReadableResourceStream') ? new ReadableResourceStream($fd, $this->loop) : new Stream($fd, $this->loop); - $stream->on('data', function ($data) use (&$contents) { - $contents .= $data; - }); - $stream->on('end', function () use (&$contents, $deferred) { - $deferred->resolve($contents); - }); - $stream->on('error', function ($error) use ($deferred) { - $deferred->reject($error); - }); - - return $deferred->promise(); - } catch (\Exception $e) { - return Promise\reject($e); - } - } -} diff --git a/vendor/react/dns/src/Config/HostsFile.php b/vendor/react/dns/src/Config/HostsFile.php index 5b6277e..c9e66da 100644 --- a/vendor/react/dns/src/Config/HostsFile.php +++ b/vendor/react/dns/src/Config/HostsFile.php @@ -73,6 +73,8 @@ public static function loadFromPathBlocking($path = null) return new self($contents); } + private $contents; + /** * Instantiate new hosts file with the given hosts file contents * diff --git a/vendor/react/dns/src/Model/HeaderBag.php b/vendor/react/dns/src/Model/HeaderBag.php deleted file mode 100644 index 0093bd3..0000000 --- a/vendor/react/dns/src/Model/HeaderBag.php +++ /dev/null @@ -1,59 +0,0 @@ - 0, - 'anCount' => 0, - 'nsCount' => 0, - 'arCount' => 0, - 'qr' => 0, - 'opcode' => Message::OPCODE_QUERY, - 'aa' => 0, - 'tc' => 0, - 'rd' => 0, - 'ra' => 0, - 'z' => 0, - 'rcode' => Message::RCODE_OK, - ); - - /** - * @deprecated unused, exists for BC only - */ - public $data = ''; - - public function get($name) - { - return isset($this->attributes[$name]) ? $this->attributes[$name] : null; - } - - public function set($name, $value) - { - $this->attributes[$name] = $value; - } - - public function isQuery() - { - return 0 === $this->attributes['qr']; - } - - public function isResponse() - { - return 1 === $this->attributes['qr']; - } - - public function isTruncated() - { - return 1 === $this->attributes['tc']; - } - - public function populateCounts(Message $message) - { - $this->attributes['qdCount'] = count($message->questions); - $this->attributes['anCount'] = count($message->answers); - $this->attributes['nsCount'] = count($message->authority); - $this->attributes['arCount'] = count($message->additional); - } -} diff --git a/vendor/react/dns/src/Model/Message.php b/vendor/react/dns/src/Model/Message.php index 167344d..df42385 100644 --- a/vendor/react/dns/src/Model/Message.php +++ b/vendor/react/dns/src/Model/Message.php @@ -4,7 +4,12 @@ use React\Dns\Query\Query; -class Message +/** + * This class represents an outgoing query message or an incoming response message + * + * @link https://tools.ietf.org/html/rfc1035#section-4.1.1 + */ +final class Message { const TYPE_A = 1; const TYPE_NS = 2; @@ -15,7 +20,9 @@ class Message const TYPE_TXT = 16; const TYPE_AAAA = 28; const TYPE_SRV = 33; + const TYPE_SSHFP = 44; const TYPE_ANY = 255; + const TYPE_CAA = 257; const CLASS_IN = 1; @@ -39,10 +46,9 @@ class Message public static function createRequestForQuery(Query $query) { $request = new Message(); - $request->header->set('id', self::generateId()); - $request->header->set('rd', 1); - $request->questions[] = (array) $query; - $request->prepare(); + $request->id = self::generateId(); + $request->rd = true; + $request->questions[] = $query; return $request; } @@ -57,20 +63,16 @@ public static function createRequestForQuery(Query $query) public static function createResponseWithAnswersForQuery(Query $query, array $answers) { $response = new Message(); - $response->header->set('id', self::generateId()); - $response->header->set('qr', 1); - $response->header->set('opcode', Message::OPCODE_QUERY); - $response->header->set('rd', 1); - $response->header->set('rcode', Message::RCODE_OK); + $response->id = self::generateId(); + $response->qr = true; + $response->rd = true; - $response->questions[] = (array) $query; + $response->questions[] = $query; foreach ($answers as $record) { $response->answers[] = $record; } - $response->prepare(); - return $response; } @@ -100,55 +102,86 @@ private static function generateId() return mt_rand(0, 0xffff); } - public $header; - public $questions = array(); - public $answers = array(); - public $authority = array(); - public $additional = array(); + /** + * The 16 bit message ID + * + * The response message ID has to match the request message ID. This allows + * the receiver to verify this is the correct response message. An outside + * attacker may try to inject fake responses by "guessing" the message ID, + * so this should use a proper CSPRNG to avoid possible cache poisoning. + * + * @var int 16 bit message ID + * @see self::generateId() + */ + public $id = 0; /** - * @deprecated still used internally for BC reasons, should not be used externally. + * @var bool Query/Response flag, query=false or response=true */ - public $data = ''; + public $qr = false; /** - * @deprecated still used internally for BC reasons, should not be used externally. + * @var int specifies the kind of query (4 bit), see self::OPCODE_* constants + * @see self::OPCODE_QUERY */ - public $consumed = 0; + public $opcode = self::OPCODE_QUERY; - public function __construct() - { - $this->header = new HeaderBag(); - } + /** + * + * @var bool Authoritative Answer + */ + public $aa = false; /** - * Returns the 16 bit message ID + * @var bool TrunCation + */ + public $tc = false; + + /** + * @var bool Recursion Desired + */ + public $rd = false; + + /** + * @var bool Recursion Available + */ + public $ra = false; + + /** + * @var int response code (4 bit), see self::RCODE_* constants + * @see self::RCODE_OK + */ + public $rcode = Message::RCODE_OK; + + /** + * An array of Query objects * - * The response message ID has to match the request message ID. This allows - * the receiver to verify this is the correct response message. An outside - * attacker may try to inject fake responses by "guessing" the message ID, - * so this should use a proper CSPRNG to avoid possible cache poisoning. + * ```php + * $questions = array( + * new Query( + * 'reactphp.org', + * Message::TYPE_A, + * Message::CLASS_IN + * ) + * ); + * ``` * - * @return int - * @see self::generateId() + * @var Query[] */ - public function getId() - { - return $this->header->get('id'); - } + public $questions = array(); /** - * Returns the response code (RCODE) - * - * @return int see self::RCODE_* constants + * @var Record[] */ - public function getResponseCode() - { - return $this->header->get('rcode'); - } + public $answers = array(); - public function prepare() - { - $this->header->populateCounts($this); - } + /** + * @var Record[] + */ + public $authority = array(); + + /** + * @var Record[] + */ + public $additional = array(); } diff --git a/vendor/react/dns/src/Model/Record.php b/vendor/react/dns/src/Model/Record.php index 7507fcb..4c357ed 100644 --- a/vendor/react/dns/src/Model/Record.php +++ b/vendor/react/dns/src/Model/Record.php @@ -2,7 +2,16 @@ namespace React\Dns\Model; -class Record +/** + * This class represents a single resulting record in a response message + * + * It uses a structure similar to `\React\Dns\Query\Query`, but does include + * fields for resulting TTL and resulting record data (IPs etc.). + * + * @link https://tools.ietf.org/html/rfc1035#section-4.1.3 + * @see \React\Dns\Query\Query + */ +final class Record { /** * @var string hostname without trailing dot, for example "reactphp.org" @@ -34,10 +43,13 @@ class Record * * - A: * IPv4 address string, for example "192.168.1.1". + * * - AAAA: * IPv6 address string, for example "::1". + * * - CNAME / PTR / NS: * The hostname without trailing dot, for example "reactphp.org". + * * - TXT: * List of string values, for example `["v=spf1 include:example.com"]`. * This is commonly a list with only a single string value, but this @@ -49,6 +61,7 @@ class Record * suggests using key-value pairs such as `["name=test","version=1"]`, but * interpretation of this is not enforced and left up to consumers of this * library (used for DNS-SD/Zeroconf and others). + * * - MX: * Mail server priority (UINT16) and target hostname without trailing dot, * for example `{"priority":10,"target":"mx.example.com"}`. @@ -57,6 +70,7 @@ class Record * referred to as exchange). If a response message contains multiple * records of this type, targets should be sorted by priority (lowest * first) - this is left up to consumers of this library (used for SMTP). + * * - SRV: * Service priority (UINT16), service weight (UINT16), service port (UINT16) * and target hostname without trailing dot, for example @@ -69,12 +83,25 @@ class Record * randomly according to their weight - this is left up to consumers of * this library, see also [RFC 2782](https://tools.ietf.org/html/rfc2782) * for more details. + * + * - SSHFP: + * Includes algorithm (UNIT8), fingerprint type (UNIT8) and fingerprint + * value as lower case hex string, for example: + * `{"algorithm":1,"type":1,"fingerprint":"0123456789abcdef..."}` + * See also https://www.iana.org/assignments/dns-sshfp-rr-parameters/dns-sshfp-rr-parameters.xhtml + * for algorithm and fingerprint type assignments. + * * - SOA: * Includes master hostname without trailing dot, responsible person email * as hostname without trailing dot and serial, refresh, retry, expire and * minimum times in seconds (UINT32 each), for example: * `{"mname":"ns.example.com","rname":"hostmaster.example.com","serial": * 2018082601,"refresh":3600,"retry":1800,"expire":60000,"minimum":3600}`. + * + * - CAA: + * Includes flag (UNIT8), tag string and value string, for example: + * `{"flag":128,"tag":"issue","value":"letsencrypt.org"}` + * * - Any other unknown type: * An opaque binary string containing the RDATA as transported in the DNS * record. For forwards compatibility, you should not rely on this format @@ -87,7 +114,14 @@ class Record */ public $data; - public function __construct($name, $type, $class, $ttl = 0, $data = null) + /** + * @param string $name + * @param int $type + * @param int $class + * @param int $ttl + * @param string|string[]|array $data + */ + public function __construct($name, $type, $class, $ttl, $data) { $this->name = $name; $this->type = $type; diff --git a/vendor/react/dns/src/Protocol/BinaryDumper.php b/vendor/react/dns/src/Protocol/BinaryDumper.php index 35d6ae6..74d0b72 100644 --- a/vendor/react/dns/src/Protocol/BinaryDumper.php +++ b/vendor/react/dns/src/Protocol/BinaryDumper.php @@ -3,60 +3,187 @@ namespace React\Dns\Protocol; use React\Dns\Model\Message; -use React\Dns\Model\HeaderBag; +use React\Dns\Model\Record; +use React\Dns\Query\Query; -class BinaryDumper +final class BinaryDumper { + /** + * @param Message $message + * @return string + */ public function toBinary(Message $message) { $data = ''; - $data .= $this->headerToBinary($message->header); + $data .= $this->headerToBinary($message); $data .= $this->questionToBinary($message->questions); + $data .= $this->recordsToBinary($message->answers); + $data .= $this->recordsToBinary($message->authority); + $data .= $this->recordsToBinary($message->additional); return $data; } - private function headerToBinary(HeaderBag $header) + /** + * @param Message $message + * @return string + */ + private function headerToBinary(Message $message) { $data = ''; - $data .= pack('n', $header->get('id')); + $data .= pack('n', $message->id); $flags = 0x00; - $flags = ($flags << 1) | $header->get('qr'); - $flags = ($flags << 4) | $header->get('opcode'); - $flags = ($flags << 1) | $header->get('aa'); - $flags = ($flags << 1) | $header->get('tc'); - $flags = ($flags << 1) | $header->get('rd'); - $flags = ($flags << 1) | $header->get('ra'); - $flags = ($flags << 3) | $header->get('z'); - $flags = ($flags << 4) | $header->get('rcode'); + $flags = ($flags << 1) | ($message->qr ? 1 : 0); + $flags = ($flags << 4) | $message->opcode; + $flags = ($flags << 1) | ($message->aa ? 1 : 0); + $flags = ($flags << 1) | ($message->tc ? 1 : 0); + $flags = ($flags << 1) | ($message->rd ? 1 : 0); + $flags = ($flags << 1) | ($message->ra ? 1 : 0); + $flags = ($flags << 3) | 0; // skip unused zero bit + $flags = ($flags << 4) | $message->rcode; $data .= pack('n', $flags); - $data .= pack('n', $header->get('qdCount')); - $data .= pack('n', $header->get('anCount')); - $data .= pack('n', $header->get('nsCount')); - $data .= pack('n', $header->get('arCount')); + $data .= pack('n', count($message->questions)); + $data .= pack('n', count($message->answers)); + $data .= pack('n', count($message->authority)); + $data .= pack('n', count($message->additional)); return $data; } + /** + * @param Query[] $questions + * @return string + */ private function questionToBinary(array $questions) { $data = ''; foreach ($questions as $question) { - $labels = explode('.', $question['name']); - foreach ($labels as $label) { - $data .= chr(strlen($label)).$label; + $data .= $this->domainNameToBinary($question->name); + $data .= pack('n*', $question->type, $question->class); + } + + return $data; + } + + /** + * @param Record[] $records + * @return string + */ + private function recordsToBinary(array $records) + { + $data = ''; + + foreach ($records as $record) { + /* @var $record Record */ + switch ($record->type) { + case Message::TYPE_A: + case Message::TYPE_AAAA: + $binary = \inet_pton($record->data); + break; + case Message::TYPE_CNAME: + case Message::TYPE_NS: + case Message::TYPE_PTR: + $binary = $this->domainNameToBinary($record->data); + break; + case Message::TYPE_TXT: + $binary = $this->textsToBinary($record->data); + break; + case Message::TYPE_MX: + $binary = \pack( + 'n', + $record->data['priority'] + ); + $binary .= $this->domainNameToBinary($record->data['target']); + break; + case Message::TYPE_SRV: + $binary = \pack( + 'n*', + $record->data['priority'], + $record->data['weight'], + $record->data['port'] + ); + $binary .= $this->domainNameToBinary($record->data['target']); + break; + case Message::TYPE_SOA: + $binary = $this->domainNameToBinary($record->data['mname']); + $binary .= $this->domainNameToBinary($record->data['rname']); + $binary .= \pack( + 'N*', + $record->data['serial'], + $record->data['refresh'], + $record->data['retry'], + $record->data['expire'], + $record->data['minimum'] + ); + break; + case Message::TYPE_CAA: + $binary = \pack( + 'C*', + $record->data['flag'], + \strlen($record->data['tag']) + ); + $binary .= $record->data['tag']; + $binary .= $record->data['value']; + break; + case Message::TYPE_SSHFP: + $binary = \pack( + 'CCH*', + $record->data['algorithm'], + $record->data['type'], + $record->data['fingerprint'] + ); + break; + default: + // RDATA is already stored as binary value for unknown record types + $binary = $record->data; } - $data .= "\x00"; - $data .= pack('n*', $question['type'], $question['class']); + $data .= $this->domainNameToBinary($record->name); + $data .= \pack('nnNn', $record->type, $record->class, $record->ttl, \strlen($binary)); + $data .= $binary; } return $data; } + + /** + * @param string[] $texts + * @return string + */ + private function textsToBinary(array $texts) + { + $data = ''; + foreach ($texts as $text) { + $data .= \chr(\strlen($text)) . $text; + } + return $data; + } + + /** + * @param string $host + * @return string + */ + private function domainNameToBinary($host) + { + if ($host === '') { + return "\0"; + } + + // break up domain name at each dot that is not preceeded by a backslash (escaped notation) + return $this->textsToBinary( + \array_map( + 'stripcslashes', + \preg_split( + '/(?data = $data; + $message->consumed = null; + if ($this->parse($data, $message) !== $message) { throw new InvalidArgumentException('Unable to parse binary message'); } - return $message; - } + unset($message->data, $message->consumed); - /** - * @deprecated unused, exists for BC only - * @codeCoverageIgnore - */ - public function parseChunk($data, Message $message) - { - return $this->parse($data, $message); + return $message; } private function parse($data, Message $message) { - $message->data .= $data; + if (!isset($message->data[12 - 1])) { + return; + } - if (!$message->header->get('id')) { - if (!$this->parseHeader($message)) { + list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', substr($message->data, 0, 12))); + $message->consumed += 12; + + $message->id = $id; + $message->rcode = $fields & 0xf; + $message->ra = (($fields >> 7) & 1) === 1; + $message->rd = (($fields >> 8) & 1) === 1; + $message->tc = (($fields >> 9) & 1) === 1; + $message->aa = (($fields >> 10) & 1) === 1; + $message->opcode = ($fields >> 11) & 0xf; + $message->qr = (($fields >> 15) & 1) === 1; + + // parse all questions + for ($i = $qdCount; $i > 0; --$i) { + $question = $this->parseQuestion($message); + if ($question === null) { return; + } else { + $message->questions[] = $question; } } - if ($message->header->get('qdCount') != count($message->questions)) { - if (!$this->parseQuestion($message)) { + // parse all answer records + for ($i = $anCount; $i > 0; --$i) { + $record = $this->parseRecord($message); + if ($record === null) { return; + } else { + $message->answers[] = $record; } } - if ($message->header->get('anCount') != count($message->answers)) { - if (!$this->parseAnswer($message)) { + // parse all authority records + for ($i = $nsCount; $i > 0; --$i) { + $record = $this->parseRecord($message); + if ($record === null) { return; + } else { + $message->authority[] = $record; } } - return $message; - } - - public function parseHeader(Message $message) - { - if (!isset($message->data[12 - 1])) { - return; - } - - $header = substr($message->data, 0, 12); - $message->consumed += 12; - - list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', $header)); - - $rcode = $fields & bindec('1111'); - $z = ($fields >> 4) & bindec('111'); - $ra = ($fields >> 7) & 1; - $rd = ($fields >> 8) & 1; - $tc = ($fields >> 9) & 1; - $aa = ($fields >> 10) & 1; - $opcode = ($fields >> 11) & bindec('1111'); - $qr = ($fields >> 15) & 1; - - $vars = compact('id', 'qdCount', 'anCount', 'nsCount', 'arCount', - 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', 'rcode'); - - - foreach ($vars as $name => $value) { - $message->header->set($name, $value); + // parse all additional records + for ($i = $arCount; $i > 0; --$i) { + $record = $this->parseRecord($message); + if ($record === null) { + return; + } else { + $message->additional[] = $record; + } } return $message; } - public function parseQuestion(Message $message) + /** + * @param Message $message + * @return ?Query + */ + private function parseQuestion(Message $message) { $consumed = $message->consumed; @@ -110,27 +117,25 @@ public function parseQuestion(Message $message) $message->consumed = $consumed; - $message->questions[] = array( - 'name' => implode('.', $labels), - 'type' => $type, - 'class' => $class, + return new Query( + implode('.', $labels), + $type, + $class ); - - if ($message->header->get('qdCount') != count($message->questions)) { - return $this->parseQuestion($message); - } - - return $message; } - public function parseAnswer(Message $message) + /** + * @param Message $message + * @return ?Record returns parsed Record on success or null if data is invalid/incomplete + */ + private function parseRecord(Message $message) { $consumed = $message->consumed; list($name, $consumed) = $this->readDomain($message->data, $consumed); if ($name === null || !isset($message->data[$consumed + 10 - 1])) { - return; + return null; } list($type, $class) = array_values(unpack('n*', substr($message->data, $consumed, 4))); @@ -148,7 +153,7 @@ public function parseAnswer(Message $message) $consumed += 2; if (!isset($message->data[$consumed + $rdLength - 1])) { - return; + return null; } $rdata = null; @@ -195,6 +200,18 @@ public function parseAnswer(Message $message) 'target' => $target ); } + } elseif (Message::TYPE_SSHFP === $type) { + if ($rdLength > 2) { + list($algorithm, $hash) = \array_values(\unpack('C*', \substr($message->data, $consumed, 2))); + $fingerprint = \bin2hex(\substr($message->data, $consumed + 2, $rdLength - 2)); + $consumed += $rdLength; + + $rdata = array( + 'algorithm' => $algorithm, + 'type' => $hash, + 'fingerprint' => $fingerprint + ); + } } elseif (Message::TYPE_SOA === $type) { list($mname, $consumed) = $this->readDomain($message->data, $consumed); list($rname, $consumed) = $this->readDomain($message->data, $consumed); @@ -213,6 +230,22 @@ public function parseAnswer(Message $message) 'minimum' => $minimum ); } + } elseif (Message::TYPE_CAA === $type) { + if ($rdLength > 3) { + list($flag, $tagLength) = array_values(unpack('C*', substr($message->data, $consumed, 2))); + + if ($tagLength > 0 && $rdLength - 2 - $tagLength > 0) { + $tag = substr($message->data, $consumed + 2, $tagLength); + $value = substr($message->data, $consumed + 2 + $tagLength, $rdLength - 2 - $tagLength); + $consumed += $rdLength; + + $rdata = array( + 'flag' => $flag, + 'tag' => $tag, + 'value' => $value + ); + } + } } else { // unknown types simply parse rdata as an opaque binary string $rdata = substr($message->data, $consumed, $rdLength); @@ -221,20 +254,12 @@ public function parseAnswer(Message $message) // ensure parsing record data consumes expact number of bytes indicated in record length if ($consumed !== $expected || $rdata === null) { - return; + return null; } $message->consumed = $consumed; - $record = new Record($name, $type, $class, $ttl, $rdata); - - $message->answers[] = $record; - - if ($message->header->get('anCount') != count($message->answers)) { - return $this->parseAnswer($message); - } - - return $message; + return new Record($name, $type, $class, $ttl, $rdata); } private function readDomain($data, $consumed) @@ -245,7 +270,19 @@ private function readDomain($data, $consumed) return array(null, null); } - return array(implode('.', $labels), $consumed); + // use escaped notation for each label part, then join using dots + return array( + \implode( + '.', + \array_map( + function ($label) { + return \addcslashes($label, "\0..\40.\177"); + }, + $labels + ) + ), + $consumed + ); } private function readLabels($data, $consumed) @@ -294,59 +331,4 @@ private function readLabels($data, $consumed) return array($labels, $consumed); } - - /** - * @deprecated unused, exists for BC only - * @codeCoverageIgnore - */ - public function isEndOfLabels($data, $consumed) - { - $length = ord(substr($data, $consumed, 1)); - return 0 === $length; - } - - /** - * @deprecated unused, exists for BC only - * @codeCoverageIgnore - */ - public function getCompressedLabel($data, $consumed) - { - list($nameOffset, $consumed) = $this->getCompressedLabelOffset($data, $consumed); - list($labels) = $this->readLabels($data, $nameOffset); - - return array($labels, $consumed); - } - - /** - * @deprecated unused, exists for BC only - * @codeCoverageIgnore - */ - public function isCompressedLabel($data, $consumed) - { - $mask = 0xc000; // 1100000000000000 - list($peek) = array_values(unpack('n', substr($data, $consumed, 2))); - - return (bool) ($peek & $mask); - } - - /** - * @deprecated unused, exists for BC only - * @codeCoverageIgnore - */ - public function getCompressedLabelOffset($data, $consumed) - { - $mask = 0x3fff; // 0011111111111111 - list($peek) = array_values(unpack('n', substr($data, $consumed, 2))); - - return array($peek & $mask, $consumed + 2); - } - - /** - * @deprecated unused, exists for BC only - * @codeCoverageIgnore - */ - public function signedLongToUnsignedLong($i) - { - return $i & 0x80000000 ? $i - 0xffffffff : $i; - } } diff --git a/vendor/react/dns/src/Query/CachedExecutor.php b/vendor/react/dns/src/Query/CachedExecutor.php deleted file mode 100644 index 285936d..0000000 --- a/vendor/react/dns/src/Query/CachedExecutor.php +++ /dev/null @@ -1,55 +0,0 @@ -executor = $executor; - $this->cache = $cache; - } - - public function query($nameserver, Query $query) - { - $executor = $this->executor; - $cache = $this->cache; - - return $this->cache - ->lookup($query) - ->then( - function ($cachedRecords) use ($query) { - return Message::createResponseWithAnswersForQuery($query, $cachedRecords); - }, - function () use ($executor, $cache, $nameserver, $query) { - return $executor - ->query($nameserver, $query) - ->then(function ($response) use ($cache, $query) { - $cache->storeResponseMessage($query->currentTime, $response); - return $response; - }); - } - ); - } - - /** - * @deprecated unused, exists for BC only - */ - public function buildResponse(Query $query, array $cachedRecords) - { - return Message::createResponseWithAnswersForQuery($query, $cachedRecords); - } - - /** - * @deprecated unused, exists for BC only - */ - protected function generateId() - { - return mt_rand(0, 0xffff); - } -} diff --git a/vendor/react/dns/src/Query/CachingExecutor.php b/vendor/react/dns/src/Query/CachingExecutor.php new file mode 100644 index 0000000..9e0bec0 --- /dev/null +++ b/vendor/react/dns/src/Query/CachingExecutor.php @@ -0,0 +1,88 @@ +executor = $executor; + $this->cache = $cache; + } + + public function query(Query $query) + { + $id = $query->name . ':' . $query->type . ':' . $query->class; + $cache = $this->cache; + $that = $this; + $executor = $this->executor; + + $pending = $cache->get($id); + return new Promise(function ($resolve, $reject) use ($query, $id, $cache, $executor, &$pending, $that) { + $pending->then( + function ($message) use ($query, $id, $cache, $executor, &$pending, $that) { + // return cached response message on cache hit + if ($message !== null) { + return $message; + } + + // perform DNS lookup if not already cached + return $pending = $executor->query($query)->then( + function (Message $message) use ($cache, $id, $that) { + // DNS response message received => store in cache when not truncated and return + if (!$message->tc) { + $cache->set($id, $message, $that->ttl($message)); + } + + return $message; + } + ); + } + )->then($resolve, function ($e) use ($reject, &$pending) { + $reject($e); + $pending = null; + }); + }, function ($_, $reject) use (&$pending, $query) { + $reject(new \RuntimeException('DNS query for ' . $query->name . ' has been cancelled')); + $pending->cancel(); + $pending = null; + }); + } + + /** + * @param Message $message + * @return int + * @internal + */ + public function ttl(Message $message) + { + // select TTL from answers (should all be the same), use smallest value if available + // @link https://tools.ietf.org/html/rfc2181#section-5.2 + $ttl = null; + foreach ($message->answers as $answer) { + if ($ttl === null || $answer->ttl < $ttl) { + $ttl = $answer->ttl; + } + } + + if ($ttl === null) { + $ttl = self::TTL; + } + + return $ttl; + } +} diff --git a/vendor/react/dns/src/Query/CancellationException.php b/vendor/react/dns/src/Query/CancellationException.php index ac30f4c..5432b36 100644 --- a/vendor/react/dns/src/Query/CancellationException.php +++ b/vendor/react/dns/src/Query/CancellationException.php @@ -2,6 +2,6 @@ namespace React\Dns\Query; -class CancellationException extends \RuntimeException +final class CancellationException extends \RuntimeException { } diff --git a/vendor/react/dns/src/Query/CoopExecutor.php b/vendor/react/dns/src/Query/CoopExecutor.php new file mode 100644 index 0000000..93c97d4 --- /dev/null +++ b/vendor/react/dns/src/Query/CoopExecutor.php @@ -0,0 +1,92 @@ +executor = $base; + } + + public function query(Query $query) + { + $key = $this->serializeQueryToIdentity($query); + if (isset($this->pending[$key])) { + // same query is already pending, so use shared reference to pending query + $promise = $this->pending[$key]; + ++$this->counts[$key]; + } else { + // no such query pending, so start new query and keep reference until it's fulfilled or rejected + $promise = $this->executor->query($query); + $this->pending[$key] = $promise; + $this->counts[$key] = 1; + + $pending =& $this->pending; + $counts =& $this->counts; + $promise->then(function () use ($key, &$pending, &$counts) { + unset($pending[$key], $counts[$key]); + }, function () use ($key, &$pending, &$counts) { + unset($pending[$key], $counts[$key]); + }); + } + + // Return a child promise awaiting the pending query. + // Cancelling this child promise should only cancel the pending query + // when no other child promise is awaiting the same query. + $pending =& $this->pending; + $counts =& $this->counts; + return new Promise(function ($resolve, $reject) use ($promise) { + $promise->then($resolve, $reject); + }, function () use (&$promise, $key, $query, &$pending, &$counts) { + if (--$counts[$key] < 1) { + unset($pending[$key], $counts[$key]); + $promise->cancel(); + $promise = null; + } + throw new \RuntimeException('DNS query for ' . $query->name . ' has been cancelled'); + }); + } + + private function serializeQueryToIdentity(Query $query) + { + return sprintf('%s:%s:%s', $query->name, $query->type, $query->class); + } +} diff --git a/vendor/react/dns/src/Query/Executor.php b/vendor/react/dns/src/Query/Executor.php deleted file mode 100644 index 40f6bb4..0000000 --- a/vendor/react/dns/src/Query/Executor.php +++ /dev/null @@ -1,160 +0,0 @@ -loop = $loop; - $this->parser = $parser; - $this->dumper = $dumper; - $this->timeout = $timeout; - } - - public function query($nameserver, Query $query) - { - $request = Message::createRequestForQuery($query); - - $queryData = $this->dumper->toBinary($request); - $transport = strlen($queryData) > 512 ? 'tcp' : 'udp'; - - return $this->doQuery($nameserver, $transport, $queryData, $query->name); - } - - /** - * @deprecated unused, exists for BC only - */ - public function prepareRequest(Query $query) - { - return Message::createRequestForQuery($query); - } - - public function doQuery($nameserver, $transport, $queryData, $name) - { - // we only support UDP right now - if ($transport !== 'udp') { - return Promise\reject(new \RuntimeException( - 'DNS query for ' . $name . ' failed: Requested transport "' . $transport . '" not available, only UDP is supported in this version' - )); - } - - $that = $this; - $parser = $this->parser; - $loop = $this->loop; - - // UDP connections are instant, so try this without a timer - try { - $conn = $this->createConnection($nameserver, $transport); - } catch (\Exception $e) { - return Promise\reject(new \RuntimeException('DNS query for ' . $name . ' failed: ' . $e->getMessage(), 0, $e)); - } - - $deferred = new Deferred(function ($resolve, $reject) use (&$timer, $loop, &$conn, $name) { - $reject(new CancellationException(sprintf('DNS query for %s has been cancelled', $name))); - - if ($timer !== null) { - $loop->cancelTimer($timer); - } - $conn->close(); - }); - - $timer = null; - if ($this->timeout !== null) { - $timer = $this->loop->addTimer($this->timeout, function () use (&$conn, $name, $deferred) { - $conn->close(); - $deferred->reject(new TimeoutException(sprintf("DNS query for %s timed out", $name))); - }); - } - - $conn->on('data', function ($data) use ($conn, $parser, $deferred, $timer, $loop, $name) { - $conn->end(); - if ($timer !== null) { - $loop->cancelTimer($timer); - } - - try { - $response = $parser->parseMessage($data); - } catch (\Exception $e) { - $deferred->reject($e); - return; - } - - if ($response->header->isTruncated()) { - $deferred->reject(new \RuntimeException('DNS query for ' . $name . ' failed: The server returned a truncated result for a UDP query, but retrying via TCP is currently not supported')); - return; - } - - $deferred->resolve($response); - }); - $conn->write($queryData); - - return $deferred->promise(); - } - - /** - * @deprecated unused, exists for BC only - */ - protected function generateId() - { - return mt_rand(0, 0xffff); - } - - /** - * @param string $nameserver - * @param string $transport - * @return \React\Stream\DuplexStreamInterface - */ - protected function createConnection($nameserver, $transport) - { - $fd = @stream_socket_client("$transport://$nameserver", $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT); - if ($fd === false) { - throw new \RuntimeException('Unable to connect to DNS server: ' . $errstr, $errno); - } - - // Instantiate stream instance around this stream resource. - // This ought to be replaced with a datagram socket in the future. - // Temporary work around for Windows 10: buffer whole UDP response - // @coverageIgnoreStart - if (!class_exists('React\Stream\Stream')) { - // prefer DuplexResourceStream as of react/stream v0.7.0 - $conn = new DuplexResourceStream($fd, $this->loop, -1); - } else { - // use legacy Stream class for react/stream < v0.7.0 - $conn = new Stream($fd, $this->loop); - $conn->bufferSize = null; - } - // @coverageIgnoreEnd - - return $conn; - } -} diff --git a/vendor/react/dns/src/Query/ExecutorInterface.php b/vendor/react/dns/src/Query/ExecutorInterface.php index 2f7a635..b356dc6 100644 --- a/vendor/react/dns/src/Query/ExecutorInterface.php +++ b/vendor/react/dns/src/Query/ExecutorInterface.php @@ -4,5 +4,40 @@ interface ExecutorInterface { - public function query($nameserver, Query $query); + /** + * Executes a query and will return a response message + * + * It returns a Promise which either fulfills with a response + * `React\Dns\Model\Message` on success or rejects with an `Exception` if + * the query is not successful. A response message may indicate an error + * condition in its `rcode`, but this is considered a valid response message. + * + * ```php + * $executor->query($query)->then( + * function (React\Dns\Model\Message $response) { + * // response message successfully received + * var_dump($response->rcode, $response->answers); + * }, + * function (Exception $error) { + * // failed to query due to $error + * } + * ); + * ``` + * + * The returned Promise MUST be implemented in such a way that it can be + * cancelled when it is still pending. Cancelling a pending promise MUST + * reject its value with an Exception. It SHOULD clean up any underlying + * resources and references as applicable. + * + * ```php + * $promise = $executor->query($query); + * + * $promise->cancel(); + * ``` + * + * @param Query $query + * @return \React\Promise\PromiseInterface<\React\Dns\Model\Message,\Exception> + * resolves with response message on success or rejects with an Exception on error + */ + public function query(Query $query); } diff --git a/vendor/react/dns/src/Query/HostsFileExecutor.php b/vendor/react/dns/src/Query/HostsFileExecutor.php index 0ca58be..d6e2d93 100644 --- a/vendor/react/dns/src/Query/HostsFileExecutor.php +++ b/vendor/react/dns/src/Query/HostsFileExecutor.php @@ -8,13 +8,13 @@ use React\Promise; /** - * Resolves hosts from the givne HostsFile or falls back to another executor + * Resolves hosts from the given HostsFile or falls back to another executor * * If the host is found in the hosts file, it will not be passed to the actual * DNS executor. If the host is not found in the hosts file, it will be passed * to the DNS executor as a fallback. */ -class HostsFileExecutor implements ExecutorInterface +final class HostsFileExecutor implements ExecutorInterface { private $hosts; private $fallback; @@ -25,7 +25,7 @@ public function __construct(HostsFile $hosts, ExecutorInterface $fallback) $this->fallback = $fallback; } - public function query($nameserver, Query $query) + public function query(Query $query) { if ($query->class === Message::CLASS_IN && ($query->type === Message::TYPE_A || $query->type === Message::TYPE_AAAA)) { // forward lookup for type A or AAAA @@ -61,7 +61,7 @@ public function query($nameserver, Query $query) } } - return $this->fallback->query($nameserver, $query); + return $this->fallback->query($query); } private function getIpFromHost($host) diff --git a/vendor/react/dns/src/Query/Query.php b/vendor/react/dns/src/Query/Query.php index 058a78d..7885023 100644 --- a/vendor/react/dns/src/Query/Query.php +++ b/vendor/react/dns/src/Query/Query.php @@ -2,32 +2,41 @@ namespace React\Dns\Query; -class Query +/** + * This class represents a single question in a query/response message + * + * It uses a structure similar to `\React\Dns\Message\Record`, but does not + * contain fields for resulting TTL and resulting record data (IPs etc.). + * + * @link https://tools.ietf.org/html/rfc1035#section-4.1.2 + * @see \React\Dns\Message\Record + */ +final class Query { + /** + * @var string query name, i.e. hostname to look up + */ public $name; + + /** + * @var int query type (aka QTYPE), see Message::TYPE_* constants + */ public $type; - public $class; /** - * @deprecated still used internally for BC reasons, should not be used externally. + * @var int query class (aka QCLASS), see Message::CLASS_IN constant */ - public $currentTime; + public $class; /** - * @param string $name query name, i.e. hostname to look up - * @param int $type query type, see Message::TYPE_* constants - * @param int $class query class, see Message::CLASS_IN constant - * @param int|null $currentTime (deprecated) still used internally, should not be passed explicitly anymore. + * @param string $name query name, i.e. hostname to look up + * @param int $type query type, see Message::TYPE_* constants + * @param int $class query class, see Message::CLASS_IN constant */ - public function __construct($name, $type, $class, $currentTime = null) + public function __construct($name, $type, $class) { - if($currentTime === null) { - $currentTime = time(); - } - $this->name = $name; $this->type = $type; $this->class = $class; - $this->currentTime = $currentTime; } } diff --git a/vendor/react/dns/src/Query/RecordBag.php b/vendor/react/dns/src/Query/RecordBag.php deleted file mode 100644 index 26007c3..0000000 --- a/vendor/react/dns/src/Query/RecordBag.php +++ /dev/null @@ -1,26 +0,0 @@ -records[] = array($currentTime + $record->ttl, $record); - } - - public function all() - { - return array_values(array_map( - function ($value) { - list($expiresAt, $record) = $value; - return $record; - }, - $this->records - )); - } -} diff --git a/vendor/react/dns/src/Query/RecordCache.php b/vendor/react/dns/src/Query/RecordCache.php deleted file mode 100644 index 85eaffd..0000000 --- a/vendor/react/dns/src/Query/RecordCache.php +++ /dev/null @@ -1,123 +0,0 @@ -cache = $cache; - } - - /** - * Looks up the cache if there's a cached answer for the given query - * - * @param Query $query - * @return PromiseInterface Promise resolves with array of Record objects on sucess - * or rejects with mixed values when query is not cached already. - */ - public function lookup(Query $query) - { - $id = $this->serializeQueryToIdentity($query); - - $expiredAt = $this->expiredAt; - - return $this->cache - ->get($id) - ->then(function ($value) use ($query, $expiredAt) { - // cache 0.5+ resolves with null on cache miss, return explicit cache miss here - if ($value === null) { - return Promise\reject(); - } - - /* @var $recordBag RecordBag */ - $recordBag = unserialize($value); - - // reject this cache hit if the query was started before the time we expired the cache? - // todo: this is a legacy left over, this value is never actually set, so this never applies. - // todo: this should probably validate the cache time instead. - if (null !== $expiredAt && $expiredAt <= $query->currentTime) { - return Promise\reject(); - } - - return $recordBag->all(); - }); - } - - /** - * Stores all records from this response message in the cache - * - * @param int $currentTime - * @param Message $message - * @uses self::storeRecord() - */ - public function storeResponseMessage($currentTime, Message $message) - { - foreach ($message->answers as $record) { - $this->storeRecord($currentTime, $record); - } - } - - /** - * Stores a single record from a response message in the cache - * - * @param int $currentTime - * @param Record $record - */ - public function storeRecord($currentTime, Record $record) - { - $id = $this->serializeRecordToIdentity($record); - - $cache = $this->cache; - - $this->cache - ->get($id) - ->then( - function ($value) { - if ($value === null) { - // cache 0.5+ cache miss resolves with null, return empty bag here - return new RecordBag(); - } - - // reuse existing bag on cache hit to append new record to it - return unserialize($value); - }, - function ($e) { - // legacy cache < 0.5 cache miss rejects promise, return empty bag here - return new RecordBag(); - } - ) - ->then(function (RecordBag $recordBag) use ($id, $currentTime, $record, $cache) { - // add a record to the existing (possibly empty) record bag and save to cache - $recordBag->set($currentTime, $record); - $cache->set($id, serialize($recordBag)); - }); - } - - public function expire($currentTime) - { - $this->expiredAt = $currentTime; - } - - public function serializeQueryToIdentity(Query $query) - { - return sprintf('%s:%s:%s', $query->name, $query->type, $query->class); - } - - public function serializeRecordToIdentity(Record $record) - { - return sprintf('%s:%s:%s', $record->name, $record->type, $record->class); - } -} diff --git a/vendor/react/dns/src/Query/RetryExecutor.php b/vendor/react/dns/src/Query/RetryExecutor.php index 46e2ef9..3f7b893 100644 --- a/vendor/react/dns/src/Query/RetryExecutor.php +++ b/vendor/react/dns/src/Query/RetryExecutor.php @@ -5,7 +5,7 @@ use React\Promise\CancellablePromiseInterface; use React\Promise\Deferred; -class RetryExecutor implements ExecutorInterface +final class RetryExecutor implements ExecutorInterface { private $executor; private $retries; @@ -16,12 +16,12 @@ public function __construct(ExecutorInterface $executor, $retries = 2) $this->retries = $retries; } - public function query($nameserver, Query $query) + public function query(Query $query) { - return $this->tryQuery($nameserver, $query, $this->retries); + return $this->tryQuery($query, $this->retries); } - public function tryQuery($nameserver, Query $query, $retries) + public function tryQuery(Query $query, $retries) { $deferred = new Deferred(function () use (&$promise) { if ($promise instanceof CancellablePromiseInterface) { @@ -35,7 +35,7 @@ public function tryQuery($nameserver, Query $query, $retries) }; $executor = $this->executor; - $errorback = function ($e) use ($deferred, &$promise, $nameserver, $query, $success, &$errorback, &$retries, $executor) { + $errorback = function ($e) use ($deferred, &$promise, $query, $success, &$errorback, &$retries, $executor) { if (!$e instanceof TimeoutException) { $errorback = null; $deferred->reject($e); @@ -62,14 +62,14 @@ public function tryQuery($nameserver, Query $query, $retries) $r->setValue($e, $trace); } else { --$retries; - $promise = $executor->query($nameserver, $query)->then( + $promise = $executor->query($query)->then( $success, $errorback ); } }; - $promise = $this->executor->query($nameserver, $query)->then( + $promise = $this->executor->query($query)->then( $success, $errorback ); diff --git a/vendor/react/dns/src/Query/SelectiveTransportExecutor.php b/vendor/react/dns/src/Query/SelectiveTransportExecutor.php new file mode 100644 index 0000000..0f0ca5d --- /dev/null +++ b/vendor/react/dns/src/Query/SelectiveTransportExecutor.php @@ -0,0 +1,85 @@ +query( + * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) + * )->then(function (Message $message) { + * foreach ($message->answers as $answer) { + * echo 'IPv6: ' . $answer->data . PHP_EOL; + * } + * }, 'printf'); + * ``` + * + * Note that this executor only implements the logic to select the correct + * transport for the given DNS query. Implementing the correct transport logic, + * implementing timeouts and any retry logic is left up to the given executors, + * see also [`UdpTransportExecutor`](#udptransportexecutor) and + * [`TcpTransportExecutor`](#tcptransportexecutor) for more details. + * + * Note that this executor is entirely async and as such allows you to execute + * any number of queries concurrently. You should probably limit the number of + * concurrent queries in your application or you're very likely going to face + * rate limitations and bans on the resolver end. For many common applications, + * you may want to avoid sending the same query multiple times when the first + * one is still pending, so you will likely want to use this in combination with + * a `CoopExecutor` like this: + * + * ```php + * $executor = new CoopExecutor( + * new SelectiveTransportExecutor( + * $datagramExecutor, + * $streamExecutor + * ) + * ); + * ``` + */ +class SelectiveTransportExecutor implements ExecutorInterface +{ + private $datagramExecutor; + private $streamExecutor; + + public function __construct(ExecutorInterface $datagramExecutor, ExecutorInterface $streamExecutor) + { + $this->datagramExecutor = $datagramExecutor; + $this->streamExecutor = $streamExecutor; + } + + public function query(Query $query) + { + $stream = $this->streamExecutor; + $pending = $this->datagramExecutor->query($query); + + return new Promise(function ($resolve, $reject) use (&$pending, $stream, $query) { + $pending->then( + $resolve, + function ($e) use (&$pending, $stream, $query, $resolve, $reject) { + if ($e->getCode() === (\defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90)) { + $pending = $stream->query($query)->then($resolve, $reject); + } else { + $reject($e); + } + } + ); + }, function () use (&$pending) { + $pending->cancel(); + $pending = null; + }); + } +} diff --git a/vendor/react/dns/src/Query/TcpTransportExecutor.php b/vendor/react/dns/src/Query/TcpTransportExecutor.php new file mode 100644 index 0000000..4ec232e --- /dev/null +++ b/vendor/react/dns/src/Query/TcpTransportExecutor.php @@ -0,0 +1,346 @@ +query( + * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) + * )->then(function (Message $message) { + * foreach ($message->answers as $answer) { + * echo 'IPv6: ' . $answer->data . PHP_EOL; + * } + * }, 'printf'); + * + * $loop->run(); + * ``` + * + * See also [example #92](examples). + * + * Note that this executor does not implement a timeout, so you will very likely + * want to use this in combination with a `TimeoutExecutor` like this: + * + * ```php + * $executor = new TimeoutExecutor( + * new TcpTransportExecutor($nameserver, $loop), + * 3.0, + * $loop + * ); + * ``` + * + * Unlike the `UdpTransportExecutor`, this class uses a reliable TCP/IP + * transport, so you do not necessarily have to implement any retry logic. + * + * Note that this executor is entirely async and as such allows you to execute + * queries concurrently. The first query will establish a TCP/IP socket + * connection to the DNS server which will be kept open for a short period. + * Additional queries will automatically reuse this existing socket connection + * to the DNS server, will pipeline multiple requests over this single + * connection and will keep an idle connection open for a short period. The + * initial TCP/IP connection overhead may incur a slight delay if you only send + * occasional queries – when sending a larger number of concurrent queries over + * an existing connection, it becomes increasingly more efficient and avoids + * creating many concurrent sockets like the UDP-based executor. You may still + * want to limit the number of (concurrent) queries in your application or you + * may be facing rate limitations and bans on the resolver end. For many common + * applications, you may want to avoid sending the same query multiple times + * when the first one is still pending, so you will likely want to use this in + * combination with a `CoopExecutor` like this: + * + * ```php + * $executor = new CoopExecutor( + * new TimeoutExecutor( + * new TcpTransportExecutor($nameserver, $loop), + * 3.0, + * $loop + * ) + * ); + * ``` + * + * > Internally, this class uses PHP's TCP/IP sockets and does not take advantage + * of [react/socket](https://github.com/reactphp/socket) purely for + * organizational reasons to avoid a cyclic dependency between the two + * packages. Higher-level components should take advantage of the Socket + * component instead of reimplementing this socket logic from scratch. + */ +class TcpTransportExecutor implements ExecutorInterface +{ + private $nameserver; + private $loop; + private $parser; + private $dumper; + + /** + * @var ?resource + */ + private $socket; + + /** + * @var Deferred[] + */ + private $pending = array(); + + /** + * @var string[] + */ + private $names = array(); + + /** + * Maximum idle time when socket is current unused (i.e. no pending queries outstanding) + * + * If a new query is to be sent during the idle period, we can reuse the + * existing socket without having to wait for a new socket connection. + * This uses a rather small, hard-coded value to not keep any unneeded + * sockets open and to not keep the loop busy longer than needed. + * + * A future implementation may take advantage of `edns-tcp-keepalive` to keep + * the socket open for longer periods. This will likely require explicit + * configuration because this may consume additional resources and also keep + * the loop busy for longer than expected in some applications. + * + * @var float + * @link https://tools.ietf.org/html/rfc7766#section-6.2.1 + * @link https://tools.ietf.org/html/rfc7828 + */ + private $idlePeriod = 0.001; + + /** + * @var ?\React\EventLoop\TimerInterface + */ + private $idleTimer; + + private $writeBuffer = ''; + private $writePending = false; + + private $readBuffer = ''; + private $readPending = false; + + /** + * @param string $nameserver + * @param LoopInterface $loop + */ + public function __construct($nameserver, LoopInterface $loop) + { + if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { + // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets + $nameserver = '[' . $nameserver . ']'; + } + + $parts = \parse_url((\strpos($nameserver, '://') === false ? 'tcp://' : '') . $nameserver); + if (!isset($parts['scheme'], $parts['host']) || $parts['scheme'] !== 'tcp' || !\filter_var(\trim($parts['host'], '[]'), \FILTER_VALIDATE_IP)) { + throw new \InvalidArgumentException('Invalid nameserver address given'); + } + + $this->nameserver = $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53); + $this->loop = $loop; + $this->parser = new Parser(); + $this->dumper = new BinaryDumper(); + } + + public function query(Query $query) + { + $request = Message::createRequestForQuery($query); + + // keep shuffing message ID to avoid using the same message ID for two pending queries at the same time + while (isset($this->pending[$request->id])) { + $request->id = \mt_rand(0, 0xffff); // @codeCoverageIgnore + } + + $queryData = $this->dumper->toBinary($request); + $length = \strlen($queryData); + if ($length > 0xffff) { + return \React\Promise\reject(new \RuntimeException( + 'DNS query for ' . $query->name . ' failed: Query too large for TCP transport' + )); + } + + $queryData = \pack('n', $length) . $queryData; + + if ($this->socket === null) { + // create async TCP/IP connection (may take a while) + $socket = @\stream_socket_client($this->nameserver, $errno, $errstr, 0, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT); + if ($socket === false) { + return \React\Promise\reject(new \RuntimeException( + 'DNS query for ' . $query->name . ' failed: Unable to connect to DNS server (' . $errstr . ')', + $errno + )); + } + + // set socket to non-blocking and wait for it to become writable (connection success/rejected) + \stream_set_blocking($socket, false); + $this->socket = $socket; + } + + if ($this->idleTimer !== null) { + $this->loop->cancelTimer($this->idleTimer); + $this->idleTimer = null; + } + + // wait for socket to become writable to actually write out data + $this->writeBuffer .= $queryData; + if (!$this->writePending) { + $this->writePending = true; + $this->loop->addWriteStream($this->socket, array($this, 'handleWritable')); + } + + $names =& $this->names; + $that = $this; + $deferred = new Deferred(function () use ($that, &$names, $request) { + // remove from list of pending names, but remember pending query + $name = $names[$request->id]; + unset($names[$request->id]); + $that->checkIdle(); + + throw new CancellationException('DNS query for ' . $name . ' has been cancelled'); + }); + + $this->pending[$request->id] = $deferred; + $this->names[$request->id] = $query->name; + + return $deferred->promise(); + } + + /** + * @internal + */ + public function handleWritable() + { + if ($this->readPending === false) { + $name = @\stream_socket_get_name($this->socket, true); + if ($name === false) { + $this->closeError('Connection to DNS server rejected'); + return; + } + + $this->readPending = true; + $this->loop->addReadStream($this->socket, array($this, 'handleRead')); + } + + $written = @\fwrite($this->socket, $this->writeBuffer); + if ($written === false || $written === 0) { + $this->closeError('Unable to write to closed socket'); + return; + } + + if (isset($this->writeBuffer[$written])) { + $this->writeBuffer = \substr($this->writeBuffer, $written); + } else { + $this->loop->removeWriteStream($this->socket); + $this->writePending = false; + $this->writeBuffer = ''; + } + } + + /** + * @internal + */ + public function handleRead() + { + // read one chunk of data from the DNS server + // any error is fatal, this is a stream of TCP/IP data + $chunk = @\fread($this->socket, 65536); + if ($chunk === false || $chunk === '') { + $this->closeError('Connection to DNS server lost'); + return; + } + + // reassemble complete message by concatenating all chunks. + $this->readBuffer .= $chunk; + + // response message header contains at least 12 bytes + while (isset($this->readBuffer[11])) { + // read response message length from first 2 bytes and ensure we have length + data in buffer + list(, $length) = \unpack('n', $this->readBuffer); + if (!isset($this->readBuffer[$length + 1])) { + return; + } + + $data = \substr($this->readBuffer, 2, $length); + $this->readBuffer = (string)substr($this->readBuffer, $length + 2); + + try { + $response = $this->parser->parseMessage($data); + } catch (\Exception $e) { + // reject all pending queries if we received an invalid message from remote server + $this->closeError('Invalid message received from DNS server'); + return; + } + + // reject all pending queries if we received an unexpected response ID or truncated response + if (!isset($this->pending[$response->id]) || $response->tc) { + $this->closeError('Invalid response message received from DNS server'); + return; + } + + $deferred = $this->pending[$response->id]; + unset($this->pending[$response->id], $this->names[$response->id]); + + $deferred->resolve($response); + + $this->checkIdle(); + } + } + + /** + * @internal + * @param string $reason + */ + public function closeError($reason) + { + $this->readBuffer = ''; + if ($this->readPending) { + $this->loop->removeReadStream($this->socket); + $this->readPending = false; + } + + $this->writeBuffer = ''; + if ($this->writePending) { + $this->loop->removeWriteStream($this->socket); + $this->writePending = false; + } + + if ($this->idleTimer !== null) { + $this->loop->cancelTimer($this->idleTimer); + $this->idleTimer = null; + } + + @\fclose($this->socket); + $this->socket = null; + + foreach ($this->names as $id => $name) { + $this->pending[$id]->reject(new \RuntimeException( + 'DNS query for ' . $name . ' failed: ' . $reason + )); + } + $this->pending = $this->names = array(); + } + + /** + * @internal + */ + public function checkIdle() + { + if ($this->idleTimer === null && !$this->names) { + $that = $this; + $this->idleTimer = $this->loop->addTimer($this->idlePeriod, function () use ($that) { + $that->closeError('Idle timeout'); + }); + } + } +} diff --git a/vendor/react/dns/src/Query/TimeoutException.php b/vendor/react/dns/src/Query/TimeoutException.php index 90bf806..109b0a9 100644 --- a/vendor/react/dns/src/Query/TimeoutException.php +++ b/vendor/react/dns/src/Query/TimeoutException.php @@ -2,6 +2,6 @@ namespace React\Dns\Query; -class TimeoutException extends \Exception +final class TimeoutException extends \Exception { } diff --git a/vendor/react/dns/src/Query/TimeoutExecutor.php b/vendor/react/dns/src/Query/TimeoutExecutor.php index 6a44888..5cee480 100644 --- a/vendor/react/dns/src/Query/TimeoutExecutor.php +++ b/vendor/react/dns/src/Query/TimeoutExecutor.php @@ -7,7 +7,7 @@ use React\Promise\CancellablePromiseInterface; use React\Promise\Timer; -class TimeoutExecutor implements ExecutorInterface +final class TimeoutExecutor implements ExecutorInterface { private $executor; private $loop; @@ -20,9 +20,9 @@ public function __construct(ExecutorInterface $executor, $timeout, LoopInterface $this->timeout = $timeout; } - public function query($nameserver, Query $query) + public function query(Query $query) { - return Timer\timeout($this->executor->query($nameserver, $query), $this->timeout, $this->loop)->then(null, function ($e) use ($query) { + return Timer\timeout($this->executor->query($query), $this->timeout, $this->loop)->then(null, function ($e) use ($query) { if ($e instanceof Timer\TimeoutException) { $e = new TimeoutException(sprintf("DNS query for %s timed out", $query->name), 0, $e); } diff --git a/vendor/react/dns/src/Query/UdpTransportExecutor.php b/vendor/react/dns/src/Query/UdpTransportExecutor.php index 16d638c..62ac218 100644 --- a/vendor/react/dns/src/Query/UdpTransportExecutor.php +++ b/vendor/react/dns/src/Query/UdpTransportExecutor.php @@ -19,10 +19,9 @@ * * ```php * $loop = Factory::create(); - * $executor = new UdpTransportExecutor($loop); + * $executor = new UdpTransportExecutor('8.8.8.8:53', $loop); * * $executor->query( - * '8.8.8.8:53', * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) * )->then(function (Message $message) { * foreach ($message->answers as $answer) { @@ -40,7 +39,7 @@ * * ```php * $executor = new TimeoutExecutor( - * new UdpTransportExecutor($loop), + * new UdpTransportExecutor($nameserver, $loop), * 3.0, * $loop * ); @@ -53,57 +52,82 @@ * ```php * $executor = new RetryExecutor( * new TimeoutExecutor( - * new UdpTransportExecutor($loop), + * new UdpTransportExecutor($nameserver, $loop), * 3.0, * $loop * ) * ); * ``` * + * Note that this executor is entirely async and as such allows you to execute + * any number of queries concurrently. You should probably limit the number of + * concurrent queries in your application or you're very likely going to face + * rate limitations and bans on the resolver end. For many common applications, + * you may want to avoid sending the same query multiple times when the first + * one is still pending, so you will likely want to use this in combination with + * a `CoopExecutor` like this: + * + * ```php + * $executor = new CoopExecutor( + * new RetryExecutor( + * new TimeoutExecutor( + * new UdpTransportExecutor($nameserver, $loop), + * 3.0, + * $loop + * ) + * ) + * ); + * ``` + * * > Internally, this class uses PHP's UDP sockets and does not take advantage * of [react/datagram](https://github.com/reactphp/datagram) purely for * organizational reasons to avoid a cyclic dependency between the two * packages. Higher-level components should take advantage of the Datagram * component instead of reimplementing this socket logic from scratch. */ -class UdpTransportExecutor implements ExecutorInterface +final class UdpTransportExecutor implements ExecutorInterface { + private $nameserver; private $loop; private $parser; private $dumper; /** - * @param LoopInterface $loop - * @param null|Parser $parser optional/advanced: DNS protocol parser to use - * @param null|BinaryDumper $dumper optional/advanced: DNS protocol dumper to use + * @param string $nameserver + * @param LoopInterface $loop */ - public function __construct(LoopInterface $loop, Parser $parser = null, BinaryDumper $dumper = null) + public function __construct($nameserver, LoopInterface $loop) { - if ($parser === null) { - $parser = new Parser(); + if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { + // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets + $nameserver = '[' . $nameserver . ']'; } - if ($dumper === null) { - $dumper = new BinaryDumper(); + + $parts = \parse_url((\strpos($nameserver, '://') === false ? 'udp://' : '') . $nameserver); + if (!isset($parts['scheme'], $parts['host']) || $parts['scheme'] !== 'udp' || !\filter_var(\trim($parts['host'], '[]'), \FILTER_VALIDATE_IP)) { + throw new \InvalidArgumentException('Invalid nameserver address given'); } + $this->nameserver = 'udp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53); $this->loop = $loop; - $this->parser = $parser; - $this->dumper = $dumper; + $this->parser = new Parser(); + $this->dumper = new BinaryDumper(); } - public function query($nameserver, Query $query) + public function query(Query $query) { $request = Message::createRequestForQuery($query); $queryData = $this->dumper->toBinary($request); if (isset($queryData[512])) { return \React\Promise\reject(new \RuntimeException( - 'DNS query for ' . $query->name . ' failed: Query too large for UDP transport' + 'DNS query for ' . $query->name . ' failed: Query too large for UDP transport', + \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90 )); } // UDP connections are instant, so try connection without a loop or timeout - $socket = @\stream_socket_client("udp://$nameserver", $errno, $errstr, 0); + $socket = @\stream_socket_client($this->nameserver, $errno, $errstr, 0); if ($socket === false) { return \React\Promise\reject(new \RuntimeException( 'DNS query for ' . $query->name . ' failed: Unable to connect to DNS server (' . $errstr . ')', @@ -140,7 +164,7 @@ public function query($nameserver, Query $query) // ignore and await next if we received an unexpected response ID // this may as well be a fake response from an attacker (possible cache poisoning) - if ($response->getId() !== $request->getId()) { + if ($response->id !== $request->id) { return; } @@ -148,8 +172,11 @@ public function query($nameserver, Query $query) $loop->removeReadStream($socket); \fclose($socket); - if ($response->header->isTruncated()) { - $deferred->reject(new \RuntimeException('DNS query for ' . $query->name . ' failed: The server returned a truncated result for a UDP query, but retrying via TCP is currently not supported')); + if ($response->tc) { + $deferred->reject(new \RuntimeException( + 'DNS query for ' . $query->name . ' failed: The server returned a truncated result for a UDP query', + \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90 + )); return; } diff --git a/vendor/react/dns/src/RecordNotFoundException.php b/vendor/react/dns/src/RecordNotFoundException.php index 0028413..3b70274 100644 --- a/vendor/react/dns/src/RecordNotFoundException.php +++ b/vendor/react/dns/src/RecordNotFoundException.php @@ -2,6 +2,6 @@ namespace React\Dns; -class RecordNotFoundException extends \Exception +final class RecordNotFoundException extends \Exception { } diff --git a/vendor/react/dns/src/Resolver/Factory.php b/vendor/react/dns/src/Resolver/Factory.php index 7b66e1d..1750aa8 100644 --- a/vendor/react/dns/src/Resolver/Factory.php +++ b/vendor/react/dns/src/Resolver/Factory.php @@ -5,35 +5,49 @@ use React\Cache\ArrayCache; use React\Cache\CacheInterface; use React\Dns\Config\HostsFile; -use React\Dns\Query\CachedExecutor; +use React\Dns\Query\CachingExecutor; +use React\Dns\Query\CoopExecutor; use React\Dns\Query\ExecutorInterface; use React\Dns\Query\HostsFileExecutor; -use React\Dns\Query\RecordCache; use React\Dns\Query\RetryExecutor; +use React\Dns\Query\SelectiveTransportExecutor; +use React\Dns\Query\TcpTransportExecutor; use React\Dns\Query\TimeoutExecutor; use React\Dns\Query\UdpTransportExecutor; use React\EventLoop\LoopInterface; -class Factory +final class Factory { + /** + * @param string $nameserver + * @param LoopInterface $loop + * @return \React\Dns\Resolver\ResolverInterface + */ public function create($nameserver, LoopInterface $loop) { - $nameserver = $this->addPortToServerIfMissing($nameserver); - $executor = $this->decorateHostsFileExecutor($this->createRetryExecutor($loop)); + $executor = $this->decorateHostsFileExecutor($this->createExecutor($nameserver, $loop)); - return new Resolver($nameserver, $executor); + return new Resolver($executor); } + /** + * @param string $nameserver + * @param LoopInterface $loop + * @param ?CacheInterface $cache + * @return \React\Dns\Resolver\ResolverInterface + */ public function createCached($nameserver, LoopInterface $loop, CacheInterface $cache = null) { + // default to keeping maximum of 256 responses in cache unless explicitly given if (!($cache instanceof CacheInterface)) { - $cache = new ArrayCache(); + $cache = new ArrayCache(256); } - $nameserver = $this->addPortToServerIfMissing($nameserver); - $executor = $this->decorateHostsFileExecutor($this->createCachedExecutor($loop, $cache)); + $executor = $this->createExecutor($nameserver, $loop); + $executor = new CachingExecutor($executor, $cache); + $executor = $this->decorateHostsFileExecutor($executor); - return new Resolver($nameserver, $executor); + return new Resolver($executor); } /** @@ -66,36 +80,44 @@ private function decorateHostsFileExecutor(ExecutorInterface $executor) return $executor; } - protected function createExecutor(LoopInterface $loop) + private function createExecutor($nameserver, LoopInterface $loop) { - return new TimeoutExecutor( - new UdpTransportExecutor($loop), - 5.0, - $loop - ); - } + $parts = \parse_url($nameserver); - protected function createRetryExecutor(LoopInterface $loop) - { - return new RetryExecutor($this->createExecutor($loop)); + if (isset($parts['scheme']) && $parts['scheme'] === 'tcp') { + $executor = $this->createTcpExecutor($nameserver, $loop); + } elseif (isset($parts['scheme']) && $parts['scheme'] === 'udp') { + $executor = $this->createUdpExecutor($nameserver, $loop); + } else { + $executor = new SelectiveTransportExecutor( + $this->createUdpExecutor($nameserver, $loop), + $this->createTcpExecutor($nameserver, $loop) + ); + } + + return new CoopExecutor($executor); } - protected function createCachedExecutor(LoopInterface $loop, CacheInterface $cache) + private function createTcpExecutor($nameserver, LoopInterface $loop) { - return new CachedExecutor($this->createRetryExecutor($loop), new RecordCache($cache)); + return new TimeoutExecutor( + new TcpTransportExecutor($nameserver, $loop), + 5.0, + $loop + ); } - protected function addPortToServerIfMissing($nameserver) + private function createUdpExecutor($nameserver, LoopInterface $loop) { - if (strpos($nameserver, '[') === false && substr_count($nameserver, ':') >= 2) { - // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets - $nameserver = '[' . $nameserver . ']'; - } - // assume a dummy scheme when checking for the port, otherwise parse_url() fails - if (parse_url('dummy://' . $nameserver, PHP_URL_PORT) === null) { - $nameserver .= ':53'; - } - - return $nameserver; + return new RetryExecutor( + new TimeoutExecutor( + new UdpTransportExecutor( + $nameserver, + $loop + ), + 5.0, + $loop + ) + ); } } diff --git a/vendor/react/dns/src/Resolver/Resolver.php b/vendor/react/dns/src/Resolver/Resolver.php index 8690972..71a9b93 100644 --- a/vendor/react/dns/src/Resolver/Resolver.php +++ b/vendor/react/dns/src/Resolver/Resolver.php @@ -6,57 +6,19 @@ use React\Dns\Query\ExecutorInterface; use React\Dns\Query\Query; use React\Dns\RecordNotFoundException; -use React\Promise\PromiseInterface; -class Resolver +/** + * @see ResolverInterface for the base interface + */ +final class Resolver implements ResolverInterface { - private $nameserver; private $executor; - public function __construct($nameserver, ExecutorInterface $executor) + public function __construct(ExecutorInterface $executor) { - $this->nameserver = $nameserver; $this->executor = $executor; } - /** - * Resolves the given $domain name to a single IPv4 address (type `A` query). - * - * ```php - * $resolver->resolve('reactphp.org')->then(function ($ip) { - * echo 'IP for reactphp.org is ' . $ip . PHP_EOL; - * }); - * ``` - * - * This is one of the main methods in this package. It sends a DNS query - * for the given $domain name to your DNS server and returns a single IP - * address on success. - * - * If the DNS server sends a DNS response message that contains more than - * one IP address for this query, it will randomly pick one of the IP - * addresses from the response. If you want the full list of IP addresses - * or want to send a different type of query, you should use the - * [`resolveAll()`](#resolveall) method instead. - * - * If the DNS server sends a DNS response message that indicates an error - * code, this method will reject with a `RecordNotFoundException`. Its - * message and code can be used to check for the response code. - * - * If the DNS communication fails and the server does not respond with a - * valid response message, this message will reject with an `Exception`. - * - * Pending DNS queries can be cancelled by cancelling its pending promise like so: - * - * ```php - * $promise = $resolver->resolve('reactphp.org'); - * - * $promise->cancel(); - * ``` - * - * @param string $domain - * @return PromiseInterface Returns a promise which resolves with a single IP address on success or - * rejects with an Exception on error. - */ public function resolve($domain) { return $this->resolveAll($domain, Message::TYPE_A)->then(function (array $ips) { @@ -64,75 +26,18 @@ public function resolve($domain) }); } - /** - * Resolves all record values for the given $domain name and query $type. - * - * ```php - * $resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) { - * echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; - * }); - * - * $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) { - * echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; - * }); - * ``` - * - * This is one of the main methods in this package. It sends a DNS query - * for the given $domain name to your DNS server and returns a list with all - * record values on success. - * - * If the DNS server sends a DNS response message that contains one or more - * records for this query, it will return a list with all record values - * from the response. You can use the `Message::TYPE_*` constants to control - * which type of query will be sent. Note that this method always returns a - * list of record values, but each record value type depends on the query - * type. For example, it returns the IPv4 addresses for type `A` queries, - * the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`, - * `CNAME` and `PTR` queries and structured data for other queries. See also - * the `Record` documentation for more details. - * - * If the DNS server sends a DNS response message that indicates an error - * code, this method will reject with a `RecordNotFoundException`. Its - * message and code can be used to check for the response code. - * - * If the DNS communication fails and the server does not respond with a - * valid response message, this message will reject with an `Exception`. - * - * Pending DNS queries can be cancelled by cancelling its pending promise like so: - * - * ```php - * $promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA); - * - * $promise->cancel(); - * ``` - * - * @param string $domain - * @return PromiseInterface Returns a promise which resolves with all record values on success or - * rejects with an Exception on error. - */ public function resolveAll($domain, $type) { $query = new Query($domain, $type, Message::CLASS_IN); $that = $this; return $this->executor->query( - $this->nameserver, $query )->then(function (Message $response) use ($query, $that) { return $that->extractValues($query, $response); }); } - /** - * @deprecated unused, exists for BC only - */ - public function extractAddress(Query $query, Message $response) - { - $addresses = $this->extractValues($query, $response); - - return $addresses[array_rand($addresses)]; - } - /** * [Internal] extract all resource record values from response for this query * @@ -145,7 +50,7 @@ public function extractAddress(Query $query, Message $response) public function extractValues(Query $query, Message $response) { // reject if response code indicates this is an error response message - $code = $response->getResponseCode(); + $code = $response->rcode; if ($code !== Message::RCODE_OK) { switch ($code) { case Message::RCODE_FORMAT_ERROR: @@ -185,14 +90,6 @@ public function extractValues(Query $query, Message $response) return array_values($addresses); } - /** - * @deprecated unused, exists for BC only - */ - public function resolveAliases(array $answers, $name) - { - return $this->valuesByNameAndType($answers, $name, Message::TYPE_A); - } - /** * @param \React\Dns\Model\Record[] $answers * @param string $name diff --git a/vendor/react/dns/src/Resolver/ResolverInterface.php b/vendor/react/dns/src/Resolver/ResolverInterface.php new file mode 100644 index 0000000..fe937dc --- /dev/null +++ b/vendor/react/dns/src/Resolver/ResolverInterface.php @@ -0,0 +1,94 @@ +resolve('reactphp.org')->then(function ($ip) { + * echo 'IP for reactphp.org is ' . $ip . PHP_EOL; + * }); + * ``` + * + * This is one of the main methods in this package. It sends a DNS query + * for the given $domain name to your DNS server and returns a single IP + * address on success. + * + * If the DNS server sends a DNS response message that contains more than + * one IP address for this query, it will randomly pick one of the IP + * addresses from the response. If you want the full list of IP addresses + * or want to send a different type of query, you should use the + * [`resolveAll()`](#resolveall) method instead. + * + * If the DNS server sends a DNS response message that indicates an error + * code, this method will reject with a `RecordNotFoundException`. Its + * message and code can be used to check for the response code. + * + * If the DNS communication fails and the server does not respond with a + * valid response message, this message will reject with an `Exception`. + * + * Pending DNS queries can be cancelled by cancelling its pending promise like so: + * + * ```php + * $promise = $resolver->resolve('reactphp.org'); + * + * $promise->cancel(); + * ``` + * + * @param string $domain + * @return \React\Promise\PromiseInterface + * resolves with a single IP address on success or rejects with an Exception on error. + */ + public function resolve($domain); + + /** + * Resolves all record values for the given $domain name and query $type. + * + * ```php + * $resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) { + * echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; + * }); + * + * $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) { + * echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; + * }); + * ``` + * + * This is one of the main methods in this package. It sends a DNS query + * for the given $domain name to your DNS server and returns a list with all + * record values on success. + * + * If the DNS server sends a DNS response message that contains one or more + * records for this query, it will return a list with all record values + * from the response. You can use the `Message::TYPE_*` constants to control + * which type of query will be sent. Note that this method always returns a + * list of record values, but each record value type depends on the query + * type. For example, it returns the IPv4 addresses for type `A` queries, + * the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`, + * `CNAME` and `PTR` queries and structured data for other queries. See also + * the `Record` documentation for more details. + * + * If the DNS server sends a DNS response message that indicates an error + * code, this method will reject with a `RecordNotFoundException`. Its + * message and code can be used to check for the response code. + * + * If the DNS communication fails and the server does not respond with a + * valid response message, this message will reject with an `Exception`. + * + * Pending DNS queries can be cancelled by cancelling its pending promise like so: + * + * ```php + * $promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA); + * + * $promise->cancel(); + * ``` + * + * @param string $domain + * @return \React\Promise\PromiseInterface + * Resolves with all record values on success or rejects with an Exception on error. + */ + public function resolveAll($domain, $type); +} diff --git a/vendor/react/dns/tests/Config/ConfigTest.php b/vendor/react/dns/tests/Config/ConfigTest.php index 8020408..31ae1f9 100644 --- a/vendor/react/dns/tests/Config/ConfigTest.php +++ b/vendor/react/dns/tests/Config/ConfigTest.php @@ -99,7 +99,11 @@ public function testParsesFileAndIgnoresCommentsAndInvalidNameserverEntries() public function testLoadsFromWmicOnWindows() { if (DIRECTORY_SEPARATOR !== '\\') { - $this->markTestSkipped('Only on Windows'); + // WMIC is Windows-only tool and not supported on other platforms + // Unix is our main platform, so we don't want to report a skipped test here (yellow) + // $this->markTestSkipped('Only on Windows'); + $this->expectOutputString(''); + return; } $config = Config::loadWmicBlocking(); diff --git a/vendor/react/dns/tests/Config/FilesystemFactoryTest.php b/vendor/react/dns/tests/Config/FilesystemFactoryTest.php deleted file mode 100644 index bb9eac7..0000000 --- a/vendor/react/dns/tests/Config/FilesystemFactoryTest.php +++ /dev/null @@ -1,70 +0,0 @@ -getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $factory = new FilesystemFactory($loop); - $factory->parseEtcResolvConf($contents)->then(function ($config) use (&$capturedConfig) { - $capturedConfig = $config; - }); - - $this->assertNotNull($capturedConfig); - $this->assertSame($expected, $capturedConfig->nameservers); - } - - /** @test */ - public function createShouldLoadStuffFromFilesystem() - { - $this->markTestIncomplete('Filesystem API is incomplete'); - - $expected = array('8.8.8.8'); - - $triggerListener = null; - $capturedConfig = null; - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop - ->expects($this->once()) - ->method('addReadStream') - ->will($this->returnCallback(function ($stream, $listener) use (&$triggerListener) { - $triggerListener = function () use ($stream, $listener) { - call_user_func($listener, $stream); - }; - })); - - $factory = new FilesystemFactory($loop); - $factory->create(__DIR__.'/../Fixtures/etc/resolv.conf')->then(function ($config) use (&$capturedConfig) { - $capturedConfig = $config; - }); - - $triggerListener(); - - $this->assertNotNull($capturedConfig); - $this->assertSame($expected, $capturedConfig->nameservers); - } -} diff --git a/vendor/react/dns/tests/FunctionalResolverTest.php b/vendor/react/dns/tests/FunctionalResolverTest.php index 7b6a37b..d4d437c 100644 --- a/vendor/react/dns/tests/FunctionalResolverTest.php +++ b/vendor/react/dns/tests/FunctionalResolverTest.php @@ -44,6 +44,34 @@ public function testResolveGoogleResolves() $this->loop->run(); } + /** + * @group internet + */ + public function testResolveGoogleOverUdpResolves() + { + $factory = new Factory($this->loop); + $this->resolver = $factory->create('udp://8.8.8.8', $this->loop); + + $promise = $this->resolver->resolve('google.com'); + $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); + + $this->loop->run(); + } + + /** + * @group internet + */ + public function testResolveGoogleOverTcpResolves() + { + $factory = new Factory($this->loop); + $this->resolver = $factory->create('tcp://8.8.8.8', $this->loop); + + $promise = $this->resolver->resolve('google.com'); + $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); + + $this->loop->run(); + } + /** * @group internet */ @@ -57,6 +85,19 @@ public function testResolveAllGoogleMxResolvesWithCache() $this->loop->run(); } + /** + * @group internet + */ + public function testResolveAllGoogleCaaResolvesWithCache() + { + $factory = new Factory(); + $this->resolver = $factory->createCached('8.8.8.8', $this->loop); + + $promise = $this->resolver->resolveAll('google.com', Message::TYPE_CAA); + $promise->then($this->expectCallableOnceWith($this->isType('array')), $this->expectCallableNever()); + + $this->loop->run(); + } /** * @group internet @@ -98,4 +139,74 @@ public function testInvalidResolverDoesNotResolveGoogle() $promise = $this->resolver->resolve('google.com'); $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); } + + public function testResolveShouldNotCauseGarbageReferencesWhenUsingInvalidNameserver() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $factory = new Factory(); + $this->resolver = $factory->create('255.255.255.255', $this->loop); + + gc_collect_cycles(); + + $promise = $this->resolver->resolve('google.com'); + unset($promise); + + $this->assertEquals(0, gc_collect_cycles()); + } + + public function testResolveCachedShouldNotCauseGarbageReferencesWhenUsingInvalidNameserver() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $factory = new Factory(); + $this->resolver = $factory->createCached('255.255.255.255', $this->loop); + + gc_collect_cycles(); + + $promise = $this->resolver->resolve('google.com'); + unset($promise); + + $this->assertEquals(0, gc_collect_cycles()); + } + + public function testCancelResolveShouldNotCauseGarbageReferences() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $factory = new Factory(); + $this->resolver = $factory->create('127.0.0.1', $this->loop); + + gc_collect_cycles(); + + $promise = $this->resolver->resolve('google.com'); + $promise->cancel(); + $promise = null; + + $this->assertEquals(0, gc_collect_cycles()); + } + + public function testCancelResolveCachedShouldNotCauseGarbageReferences() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $factory = new Factory(); + $this->resolver = $factory->createCached('127.0.0.1', $this->loop); + + gc_collect_cycles(); + + $promise = $this->resolver->resolve('google.com'); + $promise->cancel(); + $promise = null; + + $this->assertEquals(0, gc_collect_cycles()); + } } diff --git a/vendor/react/dns/tests/Model/MessageTest.php b/vendor/react/dns/tests/Model/MessageTest.php index cf3d890..a52f659 100644 --- a/vendor/react/dns/tests/Model/MessageTest.php +++ b/vendor/react/dns/tests/Model/MessageTest.php @@ -10,22 +10,20 @@ class MessageTest extends TestCase { public function testCreateRequestDesiresRecusion() { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); $request = Message::createRequestForQuery($query); - $this->assertTrue($request->header->isQuery()); - $this->assertSame(1, $request->header->get('rd')); + $this->assertFalse($request->qr); + $this->assertTrue($request->rd); } public function testCreateResponseWithNoAnswers() { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); $answers = array(); $request = Message::createResponseWithAnswersForQuery($query, $answers); - $this->assertFalse($request->header->isQuery()); - $this->assertTrue($request->header->isResponse()); - $this->assertEquals(0, $request->header->get('anCount')); - $this->assertEquals(Message::RCODE_OK, $request->getResponseCode()); + $this->assertTrue($request->qr); + $this->assertEquals(Message::RCODE_OK, $request->rcode); } } diff --git a/vendor/react/dns/tests/Protocol/BinaryDumperTest.php b/vendor/react/dns/tests/Protocol/BinaryDumperTest.php index bf60ca9..fc36026 100644 --- a/vendor/react/dns/tests/Protocol/BinaryDumperTest.php +++ b/vendor/react/dns/tests/Protocol/BinaryDumperTest.php @@ -3,31 +3,61 @@ namespace React\Tests\Dns\Protocol; use PHPUnit\Framework\TestCase; -use React\Dns\Protocol\BinaryDumper; use React\Dns\Model\Message; +use React\Dns\Model\Record; +use React\Dns\Protocol\BinaryDumper; +use React\Dns\Query\Query; class BinaryDumperTest extends TestCase { - public function testRequestToBinary() + public function testToBinaryRequestMessage() { $data = ""; $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io $data .= "00 01 00 01"; // question: type A, class IN - $expected = $this->formatHexDump(str_replace(' ', '', $data), 2); + $expected = $this->formatHexDump($data); $request = new Message(); - $request->header->set('id', 0x7262); - $request->header->set('rd', 1); + $request->id = 0x7262; + $request->rd = true; - $request->questions[] = array( - 'name' => 'igor.io', - 'type' => Message::TYPE_A, - 'class' => Message::CLASS_IN, + $request->questions[] = new Query( + 'igor.io', + Message::TYPE_A, + Message::CLASS_IN ); - $request->prepare(); + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($request); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + public function testToBinaryRequestMessageWithCustomOptForEdns0() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 00 00 00 00 01"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "00"; // additional: (empty hostname) + $data .= "00 29 03 e8 00 00 00 00 00 00 "; // additional: type OPT, class UDP size, TTL 0, no RDATA + + $expected = $this->formatHexDump($data); + + $request = new Message(); + $request->id = 0x7262; + $request->rd = true; + + $request->questions[] = new Query( + 'igor.io', + Message::TYPE_A, + Message::CLASS_IN + ); + + $request->additional[] = new Record('', 41, 1000, 0, ''); $dumper = new BinaryDumper(); $data = $dumper->toBinary($request); @@ -36,6 +66,253 @@ public function testRequestToBinary() $this->assertSame($expected, $data); } + public function testToBinaryResponseMessageWithoutRecords() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + + $expected = $this->formatHexDump($data); + + $response = new Message(); + $response->id = 0x7262; + $response->rd = true; + $response->rcode = Message::RCODE_OK; + + $response->questions[] = new Query( + 'igor.io', + Message::TYPE_A, + Message::CLASS_IN + ); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($response); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + public function testToBinaryForResponseWithSRVRecord() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 01 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 21 00 01"; // question: type SRV, class IN + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 21 00 01"; // answer: type SRV, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 0c"; // answer: rdlength 12 + $data .= "00 0a 00 14 1f 90 04 74 65 73 74 00"; // answer: rdata priority 10, weight 20, port 8080 test + + $expected = $this->formatHexDump($data); + + $response = new Message(); + $response->id = 0x7262; + $response->rd = true; + $response->rcode = Message::RCODE_OK; + + $response->questions[] = new Query( + 'igor.io', + Message::TYPE_SRV, + Message::CLASS_IN + ); + + $response->answers[] = new Record('igor.io', Message::TYPE_SRV, Message::CLASS_IN, 86400, array( + 'priority' => 10, + 'weight' => 20, + 'port' => 8080, + 'target' => 'test' + )); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($response); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + public function testToBinaryForResponseWithSOARecord() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 01 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 06 00 01"; // question: type SOA, class IN + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 06 00 01"; // answer: type SOA, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 27"; // answer: rdlength 39 + $data .= "02 6e 73 05 68 65 6c 6c 6f 00"; // answer: rdata ns.hello (mname) + $data .= "01 65 05 68 65 6c 6c 6f 00"; // answer: rdata e.hello (rname) + $data .= "78 49 28 d5 00 00 2a 30 00 00 0e 10"; // answer: rdata 2018060501, 10800, 3600 + $data .= "00 09 3e 68 00 00 0e 10"; // answer: 605800, 3600 + + $expected = $this->formatHexDump($data); + + $response = new Message(); + $response->id = 0x7262; + $response->rd = true; + $response->rcode = Message::RCODE_OK; + + $response->questions[] = new Query( + 'igor.io', + Message::TYPE_SOA, + Message::CLASS_IN + ); + + $response->answers[] = new Record('igor.io', Message::TYPE_SOA, Message::CLASS_IN, 86400, array( + 'mname' => 'ns.hello', + 'rname' => 'e.hello', + 'serial' => 2018060501, + 'refresh' => 10800, + 'retry' => 3600, + 'expire' => 605800, + 'minimum' => 3600 + )); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($response); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + public function testToBinaryForResponseWithPTRRecordWithSpecialCharactersEscaped() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 01 00 00 00 00"; // header + $data .= "08 5f 70 72 69 6e 74 65 72 04 5f 74 63 70 06 64 6e 73 2d 73 64 03 6f 72 67 00"; // question: _printer._tcp.dns-sd.org + $data .= "00 0c 00 01"; // question: type PTR, class IN + $data .= "08 5f 70 72 69 6e 74 65 72 04 5f 74 63 70 06 64 6e 73 2d 73 64 03 6f 72 67 00"; // answer: _printer._tcp.dns-sd.org + $data .= "00 0c 00 01"; // answer: type PTR, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 2f"; // answer: rdlength 47 + $data .= "14 33 72 64 2e 20 46 6c 6f 6f 72 20 43 6f 70 79 20 52 6f 6f 6d"; // answer: answer: rdata "3rd. Floor Copy Room" … + $data .= "08 5f 70 72 69 6e 74 65 72 04 5f 74 63 70 06 64 6e 73 2d 73 64 03 6f 72 67 00"; // answer: … "._printer._tcp.dns-sd.org" + + $expected = $this->formatHexDump($data); + + $response = new Message(); + $response->id = 0x7262; + $response->rd = true; + $response->rcode = Message::RCODE_OK; + + $response->questions[] = new Query( + '_printer._tcp.dns-sd.org', + Message::TYPE_PTR, + Message::CLASS_IN + ); + + $response->answers[] = new Record( + '_printer._tcp.dns-sd.org', + Message::TYPE_PTR, + Message::CLASS_IN, + 86400, + '3rd\.\ Floor\ Copy\ Room._printer._tcp.dns-sd.org' + ); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($response); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + public function testToBinaryForResponseWithMultipleAnswerRecords() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 06 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 ff 00 01"; // question: type ANY, class IN + + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 01 00 01 00 00 00 00 00 04"; // answer: type A, class IN, TTL 0, 4 bytes + $data .= "7f 00 00 01"; // answer: 127.0.0.1 + + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 1c 00 01 00 00 00 00 00 10"; // question: type AAAA, class IN, TTL 0, 16 bytes + $data .= "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01"; // answer: ::1 + + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 10 00 01 00 00 00 00 00 0c"; // answer: type TXT, class IN, TTL 0, 12 bytes + $data .= "05 68 65 6c 6c 6f 05 77 6f 72 6c 64"; // answer: hello, world + + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 0f 00 01 00 00 00 00 00 03"; // answer: type MX, class IN, TTL 0, 3 bytes + $data .= "00 00 00"; // answer: … priority 0, no target + + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io … + $data .= "01 01 00 01 00 00 00 00 00 16"; // answer: type CAA, class IN, TTL 0, 22 bytes + $data .= "00 05 69 73 73 75 65"; // answer: 0 issue … + $data .= "6c 65 74 73 65 6e 63 72 79 70 74 2e 6f 72 67"; // answer: … letsencrypt.org + + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io … + $data .= "00 2c 00 01 00 00 00 00 00 06"; // answer: type SSHFP, class IN, TTL 0, 6 bytes + $data .= "01 01 69 ac 09 0c"; // answer: algorithm 1 (RSA), type 1 (SHA-1), fingerprint "69ac090c" + + $expected = $this->formatHexDump($data); + + $response = new Message(); + $response->id = 0x7262; + $response->rd = true; + $response->rcode = Message::RCODE_OK; + + $response->questions[] = new Query( + 'igor.io', + Message::TYPE_ANY, + Message::CLASS_IN + ); + + $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 0, '127.0.0.1'); + $response->answers[] = new Record('igor.io', Message::TYPE_AAAA, Message::CLASS_IN, 0, '::1'); + $response->answers[] = new Record('igor.io', Message::TYPE_TXT, Message::CLASS_IN, 0, array('hello', 'world')); + $response->answers[] = new Record('igor.io', Message::TYPE_MX, Message::CLASS_IN, 0, array('priority' => 0, 'target' => '')); + $response->answers[] = new Record('igor.io', Message::TYPE_CAA, Message::CLASS_IN, 0, array('flag' => 0, 'tag' => 'issue', 'value' => 'letsencrypt.org')); + $response->answers[] = new Record('igor.io', Message::TYPE_SSHFP, Message::CLASS_IN, 0, array('algorithm' => 1, 'type' => '1', 'fingerprint' => '69ac090c')); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($response); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + public function testToBinaryForResponseWithAnswerAndAdditionalRecord() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 01 00 00 00 01"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 02 00 01"; // question: type NS, class IN + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 02 00 01 00 00 00 00 00 0d"; // answer: type NS, class IN, TTL 0, 10 bytes + $data .= "07 65 78 61 6d 70 6c 65 03 63 6f 6d 00"; // answer: example.com + $data .= "07 65 78 61 6d 70 6c 65 03 63 6f 6d 00"; // additional: example.com + $data .= "00 01 00 01 00 00 00 00 00 04"; // additional: type A, class IN, TTL 0, 4 bytes + $data .= "7f 00 00 01"; // additional: 127.0.0.1 + + $expected = $this->formatHexDump($data); + + $response = new Message(); + $response->id = 0x7262; + $response->rd = true; + $response->rcode = Message::RCODE_OK; + + $response->questions[] = new Query( + 'igor.io', + Message::TYPE_NS, + Message::CLASS_IN + ); + + $response->answers[] = new Record('igor.io', Message::TYPE_NS, Message::CLASS_IN, 0, 'example.com'); + $response->additional[] = new Record('example.com', Message::TYPE_A, Message::CLASS_IN, 0, '127.0.0.1'); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($response); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + private function convertBinaryToHexDump($input) { return $this->formatHexDump(implode('', unpack('H*', $input))); @@ -43,6 +320,6 @@ private function convertBinaryToHexDump($input) private function formatHexDump($input) { - return implode(' ', str_split($input, 2)); + return implode(' ', str_split(str_replace(' ', '', $input), 2)); } } diff --git a/vendor/react/dns/tests/Protocol/ParserTest.php b/vendor/react/dns/tests/Protocol/ParserTest.php index aed4e45..6ff66a9 100644 --- a/vendor/react/dns/tests/Protocol/ParserTest.php +++ b/vendor/react/dns/tests/Protocol/ParserTest.php @@ -42,25 +42,22 @@ public function testParseRequest() $request = $this->parser->parseMessage($data); - $header = $request->header; - $this->assertSame(0x7262, $header->get('id')); - $this->assertSame(1, $header->get('qdCount')); - $this->assertSame(0, $header->get('anCount')); - $this->assertSame(0, $header->get('nsCount')); - $this->assertSame(0, $header->get('arCount')); - $this->assertSame(0, $header->get('qr')); - $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); - $this->assertSame(0, $header->get('aa')); - $this->assertSame(0, $header->get('tc')); - $this->assertSame(1, $header->get('rd')); - $this->assertSame(0, $header->get('ra')); - $this->assertSame(0, $header->get('z')); - $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + $this->assertFalse(isset($request->data)); + $this->assertFalse(isset($request->consumed)); + + $this->assertSame(0x7262, $request->id); + $this->assertSame(false, $request->qr); + $this->assertSame(Message::OPCODE_QUERY, $request->opcode); + $this->assertSame(false, $request->aa); + $this->assertSame(false, $request->tc); + $this->assertSame(true, $request->rd); + $this->assertSame(false, $request->ra); + $this->assertSame(Message::RCODE_OK, $request->rcode); $this->assertCount(1, $request->questions); - $this->assertSame('igor.io', $request->questions[0]['name']); - $this->assertSame(Message::TYPE_A, $request->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $request->questions[0]['class']); + $this->assertSame('igor.io', $request->questions[0]->name); + $this->assertSame(Message::TYPE_A, $request->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $request->questions[0]->class); } public function testParseResponse() @@ -79,25 +76,19 @@ public function testParseResponse() $response = $this->parser->parseMessage($data); - $header = $response->header; - $this->assertSame(0x7262, $header->get('id')); - $this->assertSame(1, $header->get('qdCount')); - $this->assertSame(1, $header->get('anCount')); - $this->assertSame(0, $header->get('nsCount')); - $this->assertSame(0, $header->get('arCount')); - $this->assertSame(1, $header->get('qr')); - $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); - $this->assertSame(0, $header->get('aa')); - $this->assertSame(0, $header->get('tc')); - $this->assertSame(1, $header->get('rd')); - $this->assertSame(1, $header->get('ra')); - $this->assertSame(0, $header->get('z')); - $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + $this->assertSame(0x7262, $response->id); + $this->assertSame(true, $response->qr); + $this->assertSame(Message::OPCODE_QUERY, $response->opcode); + $this->assertSame(false, $response->aa); + $this->assertSame(false, $response->tc); + $this->assertSame(true, $response->rd); + $this->assertSame(true, $response->ra); + $this->assertSame(Message::RCODE_OK, $response->rcode); $this->assertCount(1, $response->questions); - $this->assertSame('igor.io', $response->questions[0]['name']); - $this->assertSame(Message::TYPE_A, $response->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + $this->assertSame('igor.io', $response->questions[0]->name); + $this->assertSame(Message::TYPE_A, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -107,9 +98,10 @@ public function testParseResponse() $this->assertSame('178.79.169.131', $response->answers[0]->data); } - public function testParseQuestionWithTwoQuestions() + public function testParseRequestWithTwoQuestions() { $data = ""; + $data .= "72 62 01 00 00 02 00 00 00 00 00 00"; // header $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io $data .= "00 01 00 01"; // question: type A, class IN $data .= "03 77 77 77 04 69 67 6f 72 02 69 6f 00"; // question: www.igor.io @@ -117,19 +109,15 @@ public function testParseQuestionWithTwoQuestions() $data = $this->convertTcpDumpToBinary($data); - $request = new Message(); - $request->header->set('qdCount', 2); - $request->data = $data; - - $this->parser->parseQuestion($request); + $request = $this->parser->parseMessage($data); $this->assertCount(2, $request->questions); - $this->assertSame('igor.io', $request->questions[0]['name']); - $this->assertSame(Message::TYPE_A, $request->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $request->questions[0]['class']); - $this->assertSame('www.igor.io', $request->questions[1]['name']); - $this->assertSame(Message::TYPE_A, $request->questions[1]['type']); - $this->assertSame(Message::CLASS_IN, $request->questions[1]['class']); + $this->assertSame('igor.io', $request->questions[0]->name); + $this->assertSame(Message::TYPE_A, $request->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $request->questions[0]->class); + $this->assertSame('www.igor.io', $request->questions[1]->name); + $this->assertSame(Message::TYPE_A, $request->questions[1]->type); + $this->assertSame(Message::CLASS_IN, $request->questions[1]->class); } public function testParseAnswerWithInlineData() @@ -141,13 +129,7 @@ public function testParseAnswerWithInlineData() $data .= "00 04"; // answer: rdlength 4 $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -166,13 +148,7 @@ public function testParseAnswerWithExcessiveTtlReturnsZeroTtl() $data .= "00 04"; // answer: rdlength 4 $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -191,13 +167,7 @@ public function testParseAnswerWithTtlExactlyBoundaryReturnsZeroTtl() $data .= "00 04"; // answer: rdlength 4 $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -216,13 +186,7 @@ public function testParseAnswerWithMaximumTtlReturnsExactTtl() $data .= "00 04"; // answer: rdlength 4 $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -241,13 +205,7 @@ public function testParseAnswerWithUnknownType() $data .= "00 05"; // answer: rdlength 5 $data .= "68 65 6c 6c 6f"; // answer: rdata "hello" - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -275,9 +233,9 @@ public function testParseResponseWithCnameAndOffsetPointers() $response = $this->parser->parseMessage($data); $this->assertCount(1, $response->questions); - $this->assertSame('mail.google.com', $response->questions[0]['name']); - $this->assertSame(Message::TYPE_CNAME, $response->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + $this->assertSame('mail.google.com', $response->questions[0]->name); + $this->assertSame(Message::TYPE_CNAME, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); $this->assertCount(1, $response->answers); $this->assertSame('mail.google.com', $response->answers[0]->name); @@ -303,25 +261,19 @@ public function testParseAAAAResponse() $response = $this->parser->parseMessage($data); - $header = $response->header; - $this->assertSame(0xcd72, $header->get('id')); - $this->assertSame(1, $header->get('qdCount')); - $this->assertSame(1, $header->get('anCount')); - $this->assertSame(0, $header->get('nsCount')); - $this->assertSame(0, $header->get('arCount')); - $this->assertSame(1, $header->get('qr')); - $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); - $this->assertSame(0, $header->get('aa')); - $this->assertSame(0, $header->get('tc')); - $this->assertSame(1, $header->get('rd')); - $this->assertSame(1, $header->get('ra')); - $this->assertSame(0, $header->get('z')); - $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + $this->assertSame(0xcd72, $response->id); + $this->assertSame(true, $response->qr); + $this->assertSame(Message::OPCODE_QUERY, $response->opcode); + $this->assertSame(false, $response->aa); + $this->assertSame(false, $response->tc); + $this->assertSame(true, $response->rd); + $this->assertSame(true, $response->ra); + $this->assertSame(Message::RCODE_OK, $response->rcode); $this->assertCount(1, $response->questions); - $this->assertSame('google.com', $response->questions[0]['name']); - $this->assertSame(Message::TYPE_AAAA, $response->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + $this->assertSame('google.com', $response->questions[0]->name); + $this->assertSame(Message::TYPE_AAAA, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); $this->assertCount(1, $response->answers); $this->assertSame('google.com', $response->answers[0]->name); @@ -340,13 +292,7 @@ public function testParseTXTResponse() $data .= "00 06"; // answer: rdlength 6 $data .= "05 68 65 6c 6c 6f"; // answer: rdata length 5: hello - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -365,13 +311,7 @@ public function testParseTXTResponseMultiple() $data .= "00 0C"; // answer: rdlength 12 $data .= "05 68 65 6c 6c 6f 05 77 6f 72 6c 64"; // answer: rdata length 5: hello, length 5: world - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -390,13 +330,7 @@ public function testParseMXResponse() $data .= "00 09"; // answer: rdlength 9 $data .= "00 0a 05 68 65 6c 6c 6f 00"; // answer: rdata priority 10: hello - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -415,13 +349,7 @@ public function testParseSRVResponse() $data .= "00 0C"; // answer: rdlength 12 $data .= "00 0a 00 14 1F 90 04 74 65 73 74 00"; // answer: rdata priority 10, weight 20, port 8080 test - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -439,7 +367,7 @@ public function testParseSRVResponse() ); } - public function testParseResponseWithTwoAnswers() + public function testParseMessageResponseWithTwoAnswers() { $data = ""; $data .= "bc 73 81 80 00 01 00 02 00 00 00 00"; // header @@ -462,9 +390,9 @@ public function testParseResponseWithTwoAnswers() $response = $this->parser->parseMessage($data); $this->assertCount(1, $response->questions); - $this->assertSame('io.whois-servers.net', $response->questions[0]['name']); - $this->assertSame(Message::TYPE_A, $response->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + $this->assertSame('io.whois-servers.net', $response->questions[0]->name); + $this->assertSame(Message::TYPE_A, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); $this->assertCount(2, $response->answers); @@ -481,6 +409,95 @@ public function testParseResponseWithTwoAnswers() $this->assertSame('193.223.78.152', $response->answers[1]->data); } + public function testParseMessageResponseWithTwoAuthorityRecords() + { + $data = ""; + $data .= "bc 73 81 80 00 01 00 00 00 02 00 00"; // header + $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00"; + // question: io.whois-servers.net + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "c0 0c"; // authority: offset pointer to io.whois-servers.net + $data .= "00 05 00 01"; // authority: type CNAME, class IN + $data .= "00 00 00 29"; // authority: ttl 41 + $data .= "00 0e"; // authority: rdlength 14 + $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // authority: rdata whois.nic.io + $data .= "c0 32"; // authority: offset pointer to whois.nic.io + $data .= "00 01 00 01"; // authority: type CNAME, class IN + $data .= "00 00 0d f7"; // authority: ttl 3575 + $data .= "00 04"; // authority: rdlength 4 + $data .= "c1 df 4e 98"; // authority: rdata 193.223.78.152 + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $this->assertCount(1, $response->questions); + $this->assertSame('io.whois-servers.net', $response->questions[0]->name); + $this->assertSame(Message::TYPE_A, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); + + $this->assertCount(0, $response->answers); + + $this->assertCount(2, $response->authority); + + $this->assertSame('io.whois-servers.net', $response->authority[0]->name); + $this->assertSame(Message::TYPE_CNAME, $response->authority[0]->type); + $this->assertSame(Message::CLASS_IN, $response->authority[0]->class); + $this->assertSame(41, $response->authority[0]->ttl); + $this->assertSame('whois.nic.io', $response->authority[0]->data); + + $this->assertSame('whois.nic.io', $response->authority[1]->name); + $this->assertSame(Message::TYPE_A, $response->authority[1]->type); + $this->assertSame(Message::CLASS_IN, $response->authority[1]->class); + $this->assertSame(3575, $response->authority[1]->ttl); + $this->assertSame('193.223.78.152', $response->authority[1]->data); + } + + public function testParseMessageResponseWithAnswerAndAdditionalRecord() + { + $data = ""; + $data .= "bc 73 81 80 00 01 00 01 00 00 00 01"; // header + $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00"; + // question: io.whois-servers.net + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "c0 0c"; // answer: offset pointer to io.whois-servers.net + $data .= "00 05 00 01"; // answer: type CNAME, class IN + $data .= "00 00 00 29"; // answer: ttl 41 + $data .= "00 0e"; // answer: rdlength 14 + $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // answer: rdata whois.nic.io + $data .= "c0 32"; // additional: offset pointer to whois.nic.io + $data .= "00 01 00 01"; // additional: type CNAME, class IN + $data .= "00 00 0d f7"; // additional: ttl 3575 + $data .= "00 04"; // additional: rdlength 4 + $data .= "c1 df 4e 98"; // additional: rdata 193.223.78.152 + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $this->assertCount(1, $response->questions); + $this->assertSame('io.whois-servers.net', $response->questions[0]->name); + $this->assertSame(Message::TYPE_A, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); + + $this->assertCount(1, $response->answers); + + $this->assertSame('io.whois-servers.net', $response->answers[0]->name); + $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(41, $response->answers[0]->ttl); + $this->assertSame('whois.nic.io', $response->answers[0]->data); + + $this->assertCount(0, $response->authority); + $this->assertCount(1, $response->additional); + + $this->assertSame('whois.nic.io', $response->additional[0]->name); + $this->assertSame(Message::TYPE_A, $response->additional[0]->type); + $this->assertSame(Message::CLASS_IN, $response->additional[0]->class); + $this->assertSame(3575, $response->additional[0]->ttl); + $this->assertSame('193.223.78.152', $response->additional[0]->data); + } + public function testParseNSResponse() { $data = ""; @@ -490,13 +507,7 @@ public function testParseNSResponse() $data .= "00 07"; // answer: rdlength 7 $data .= "05 68 65 6c 6c 6f 00"; // answer: rdata hello - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -506,6 +517,25 @@ public function testParseNSResponse() $this->assertSame('hello', $response->answers[0]->data); } + public function testParseSSHFPResponse() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 2c 00 01"; // answer: type SSHFP, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 06"; // answer: rdlength 6 + $data .= "01 01 69 ac 09 0c"; // answer: algorithm 1 (RSA), type 1 (SHA-1), fingerprint "69ac090c" + + $response = $this->parseAnswer($data); + + $this->assertCount(1, $response->answers); + $this->assertSame('igor.io', $response->answers[0]->name); + $this->assertSame(Message::TYPE_SSHFP, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86400, $response->answers[0]->ttl); + $this->assertSame(array('algorithm' => 1, 'type' => 1, 'fingerprint' => '69ac090c'), $response->answers[0]->data); + } + public function testParseSOAResponse() { $data = ""; @@ -518,13 +548,7 @@ public function testParseSOAResponse() $data .= "78 49 28 D5 00 00 2a 30 00 00 0e 10"; // answer: rdata 2018060501, 10800, 3600 $data .= "00 09 3a 80 00 00 0e 10"; // answer: 605800, 3600 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); + $response = $this->parseAnswer($data); $this->assertCount(1, $response->answers); $this->assertSame('igor.io', $response->answers[0]->name); @@ -545,6 +569,26 @@ public function testParseSOAResponse() ); } + public function testParseCAAResponse() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "01 01 00 01"; // answer: type CAA, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 16"; // answer: rdlength 22 + $data .= "00 05 69 73 73 75 65"; // answer: rdata 0, issue + $data .= "6c 65 74 73 65 6e 63 72 79 70 74 2e 6f 72 67"; // answer: letsencrypt.org + + $response = $this->parseAnswer($data); + + $this->assertCount(1, $response->answers); + $this->assertSame('igor.io', $response->answers[0]->name); + $this->assertSame(Message::TYPE_CAA, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86400, $response->answers[0]->ttl); + $this->assertSame(array('flag' => 0, 'tag' => 'issue', 'value' => 'letsencrypt.org'), $response->answers[0]->data); + } + public function testParsePTRResponse() { $data = ""; @@ -563,25 +607,19 @@ public function testParsePTRResponse() $response = $this->parser->parseMessage($data); - $header = $response->header; - $this->assertSame(0x5dd8, $header->get('id')); - $this->assertSame(1, $header->get('qdCount')); - $this->assertSame(1, $header->get('anCount')); - $this->assertSame(0, $header->get('nsCount')); - $this->assertSame(0, $header->get('arCount')); - $this->assertSame(1, $header->get('qr')); - $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); - $this->assertSame(0, $header->get('aa')); - $this->assertSame(0, $header->get('tc')); - $this->assertSame(1, $header->get('rd')); - $this->assertSame(1, $header->get('ra')); - $this->assertSame(0, $header->get('z')); - $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + $this->assertSame(0x5dd8, $response->id); + $this->assertSame(true, $response->qr); + $this->assertSame(Message::OPCODE_QUERY, $response->opcode); + $this->assertSame(false, $response->aa); + $this->assertSame(false, $response->tc); + $this->assertSame(true, $response->rd); + $this->assertSame(true, $response->ra); + $this->assertSame(Message::RCODE_OK, $response->rcode); $this->assertCount(1, $response->questions); - $this->assertSame('4.4.8.8.in-addr.arpa', $response->questions[0]['name']); - $this->assertSame(Message::TYPE_PTR, $response->questions[0]['type']); - $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + $this->assertSame('4.4.8.8.in-addr.arpa', $response->questions[0]->name); + $this->assertSame(Message::TYPE_PTR, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); $this->assertCount(1, $response->answers); $this->assertSame('4.4.8.8.in-addr.arpa', $response->answers[0]->name); @@ -591,6 +629,36 @@ public function testParsePTRResponse() $this->assertSame('google-public-dns-b.google.com', $response->answers[0]->data); } + public function testParsePTRResponseWithSpecialCharactersEscaped() + { + $data = ""; + $data .= "5d d8 81 80 00 01 00 01 00 00 00 00"; // header + $data .= "08 5f 70 72 69 6e 74 65 72 04 5f 74 63 70 06 64 6e 73 2d 73 64 03 6f 72 67 00"; // question: _printer._tcp.dns-sd.org + $data .= "00 0c 00 01"; // question: type PTR, class IN + $data .= "c0 0c"; // answer: offset pointer to rdata + $data .= "00 0c 00 01"; // answer: type PTR, class IN + $data .= "00 01 51 7f"; // answer: ttl 86399 + $data .= "00 17"; // answer: rdlength 23 + $data .= "14 33 72 64 2e 20 46 6c 6f 6f 72 20 43 6f 70 79 20 52 6f 6f 6d"; // answer: rdata "3rd. Floor Copy Room" … + $data .= "c0 0c"; // answer: offset pointer to rdata + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $this->assertCount(1, $response->questions); + $this->assertSame('_printer._tcp.dns-sd.org', $response->questions[0]->name); + $this->assertSame(Message::TYPE_PTR, $response->questions[0]->type); + $this->assertSame(Message::CLASS_IN, $response->questions[0]->class); + + $this->assertCount(1, $response->answers); + $this->assertSame('_printer._tcp.dns-sd.org', $response->answers[0]->name); + $this->assertSame(Message::TYPE_PTR, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86399, $response->answers[0]->ttl); + $this->assertSame('3rd\.\ Floor\ Copy\ Room._printer._tcp.dns-sd.org', $response->answers[0]->data); + } + /** * @expectedException InvalidArgumentException */ @@ -706,6 +774,38 @@ public function testParseIncompleteAnswerFieldsThrows() $this->parser->parseMessage($data); } + /** + * @expectedException InvalidArgumentException + */ + public function testParseMessageResponseWithIncompleteAuthorityRecordThrows() + { + $data = ""; + $data .= "72 62 81 80 00 01 00 00 00 01 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "c0 0c"; // authority: offset pointer to igor.io + + $data = $this->convertTcpDumpToBinary($data); + + $this->parser->parseMessage($data); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testParseMessageResponseWithIncompleteAdditionalRecordThrows() + { + $data = ""; + $data .= "72 62 81 80 00 01 00 00 00 00 00 01"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "c0 0c"; // additional: offset pointer to igor.io + + $data = $this->convertTcpDumpToBinary($data); + + $this->parser->parseMessage($data); + } + /** * @expectedException InvalidArgumentException */ @@ -725,6 +825,9 @@ public function testParseIncompleteAnswerRecordDataThrows() $this->parser->parseMessage($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidNSResponseWhereDomainNameIsMissing() { $data = ""; @@ -733,17 +836,12 @@ public function testParseInvalidNSResponseWhereDomainNameIsMissing() $data .= "00 01 51 80"; // answer: ttl 86400 $data .= "00 00"; // answer: rdlength 0 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidAResponseWhereIPIsMissing() { $data = ""; @@ -752,17 +850,12 @@ public function testParseInvalidAResponseWhereIPIsMissing() $data .= "00 01 51 80"; // answer: ttl 86400 $data .= "00 00"; // answer: rdlength 0 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidAAAAResponseWhereIPIsMissing() { $data = ""; @@ -771,17 +864,12 @@ public function testParseInvalidAAAAResponseWhereIPIsMissing() $data .= "00 01 51 80"; // answer: ttl 86400 $data .= "00 00"; // answer: rdlength 0 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidTXTResponseWhereTxtChunkExceedsLimit() { $data = ""; @@ -791,17 +879,12 @@ public function testParseInvalidTXTResponseWhereTxtChunkExceedsLimit() $data .= "00 06"; // answer: rdlength 6 $data .= "06 68 65 6c 6c 6f 6f"; // answer: rdata length 6: helloo - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidMXResponseWhereDomainNameIsIncomplete() { $data = ""; @@ -811,17 +894,12 @@ public function testParseInvalidMXResponseWhereDomainNameIsIncomplete() $data .= "00 08"; // answer: rdlength 8 $data .= "00 0a 05 68 65 6c 6c 6f"; // answer: rdata priority 10: hello (missing label end) - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidMXResponseWhereDomainNameIsMissing() { $data = ""; @@ -831,17 +909,12 @@ public function testParseInvalidMXResponseWhereDomainNameIsMissing() $data .= "00 02"; // answer: rdlength 2 $data .= "00 0a"; // answer: rdata priority 10 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidSRVResponseWhereDomainNameIsIncomplete() { $data = ""; @@ -851,17 +924,12 @@ public function testParseInvalidSRVResponseWhereDomainNameIsIncomplete() $data .= "00 0b"; // answer: rdlength 11 $data .= "00 0a 00 14 1F 90 04 74 65 73 74"; // answer: rdata priority 10, weight 20, port 8080 test (missing label end) - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; - - $this->parser->parseAnswer($response); - - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidSRVResponseWhereDomainNameIsMissing() { $data = ""; @@ -871,17 +939,27 @@ public function testParseInvalidSRVResponseWhereDomainNameIsMissing() $data .= "00 06"; // answer: rdlength 6 $data .= "00 0a 00 14 1F 90"; // answer: rdata priority 10, weight 20, port 8080 - $data = $this->convertTcpDumpToBinary($data); - - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; + $this->parseAnswer($data); + } - $this->parser->parseAnswer($response); + /** + * @expectedException InvalidArgumentException + */ + public function testParseInvalidSSHFPResponseWhereRecordIsTooSmall() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 2c 00 01"; // answer: type SSHFP, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 02"; // answer: rdlength 2 + $data .= "01 01"; // answer: algorithm 1 (RSA), type 1 (SHA), missing fingerprint - $this->assertCount(0, $response->answers); + $this->parseAnswer($data); } + /** + * @expectedException InvalidArgumentException + */ public function testParseInvalidSOAResponseWhereFlagsAreMissing() { $data = ""; @@ -892,15 +970,52 @@ public function testParseInvalidSOAResponseWhereFlagsAreMissing() $data .= "02 6e 73 05 68 65 6c 6c 6f 00"; // answer: rdata ns.hello (mname) $data .= "01 65 05 68 65 6c 6c 6f 00"; // answer: rdata e.hello (rname) - $data = $this->convertTcpDumpToBinary($data); + $this->parseAnswer($data); + } - $response = new Message(); - $response->header->set('anCount', 1); - $response->data = $data; + /** + * @expectedException InvalidArgumentException + */ + public function testParseInvalidCAAResponseEmtpyData() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "01 01 00 01"; // answer: type CAA, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 00"; // answer: rdlength 0 - $this->parser->parseAnswer($response); + $this->parseAnswer($data); + } - $this->assertCount(0, $response->answers); + /** + * @expectedException InvalidArgumentException + */ + public function testParseInvalidCAAResponseMissingValue() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "01 01 00 01"; // answer: type CAA, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 07"; // answer: rdlength 22 + $data .= "00 05 69 73 73 75 65"; // answer: rdata 0, issue + + $this->parseAnswer($data); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testParseInvalidCAAResponseIncompleteTag() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "01 01 00 01"; // answer: type CAA, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 0c"; // answer: rdlength 22 + $data .= "00 ff 69 73 73 75 65"; // answer: rdata 0, issue (incomplete due to invalid tag length) + $data .= "68 65 6c 6c 6f"; // answer: hello + + $this->parseAnswer($data); } private function convertTcpDumpToBinary($input) @@ -909,4 +1024,14 @@ private function convertTcpDumpToBinary($input) return pack('H*', str_replace(' ', '', $input)); } + + private function parseAnswer($answerData) + { + $data = "72 62 81 80 00 00 00 01 00 00 00 00"; // header with one answer only + $data .= $answerData; + + $data = $this->convertTcpDumpToBinary($data); + + return $this->parser->parseMessage($data); + } } diff --git a/vendor/react/dns/tests/Query/CachedExecutorTest.php b/vendor/react/dns/tests/Query/CachedExecutorTest.php deleted file mode 100644 index d08ed05..0000000 --- a/vendor/react/dns/tests/Query/CachedExecutorTest.php +++ /dev/null @@ -1,100 +0,0 @@ -createExecutorMock(); - $executor - ->expects($this->once()) - ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnValue($this->createPromiseMock())); - - $cache = $this->getMockBuilder('React\Dns\Query\RecordCache') - ->disableOriginalConstructor() - ->getMock(); - $cache - ->expects($this->once()) - ->method('lookup') - ->will($this->returnValue(Promise\reject())); - $cachedExecutor = new CachedExecutor($executor, $cache); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $cachedExecutor->query('8.8.8.8', $query); - } - - /** - * @covers React\Dns\Query\CachedExecutor - * @test - */ - public function callingQueryTwiceShouldUseCachedResult() - { - $cachedRecords = array(new Record('igor.io', Message::TYPE_A, Message::CLASS_IN)); - - $executor = $this->createExecutorMock(); - $executor - ->expects($this->once()) - ->method('query') - ->will($this->callQueryCallbackWithAddress('178.79.169.131')); - - $cache = $this->getMockBuilder('React\Dns\Query\RecordCache') - ->disableOriginalConstructor() - ->getMock(); - $cache - ->expects($this->at(0)) - ->method('lookup') - ->with($this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnValue(Promise\reject())); - $cache - ->expects($this->at(1)) - ->method('storeResponseMessage') - ->with($this->isType('integer'), $this->isInstanceOf('React\Dns\Model\Message')); - $cache - ->expects($this->at(2)) - ->method('lookup') - ->with($this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnValue(Promise\resolve($cachedRecords))); - - $cachedExecutor = new CachedExecutor($executor, $cache); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $cachedExecutor->query('8.8.8.8', $query, function () {}, function () {}); - $cachedExecutor->query('8.8.8.8', $query, function () {}, function () {}); - } - - private function callQueryCallbackWithAddress($address) - { - return $this->returnCallback(function ($nameserver, $query) use ($address) { - $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); - $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, $address); - - return Promise\resolve($response); - }); - } - - private function createExecutorMock() - { - return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); - } - - private function createPromiseMock() - { - return $this->getMockBuilder('React\Promise\PromiseInterface')->getMock(); - } -} diff --git a/vendor/react/dns/tests/Query/CachingExecutorTest.php b/vendor/react/dns/tests/Query/CachingExecutorTest.php new file mode 100644 index 0000000..b0aa4c2 --- /dev/null +++ b/vendor/react/dns/tests/Query/CachingExecutorTest.php @@ -0,0 +1,183 @@ +getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->never())->method('query'); + + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn(new Promise(function () { })); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableNever(), $this->expectCallableNever()); + } + + public function testQueryWillReturnPendingPromiseWhenCacheReturnsMissAndWillSendSameQueryToFallbackExecutor() + { + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->once())->method('query')->with($query)->willReturn(new Promise(function () { })); + + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->willReturn(\React\Promise\resolve(null)); + + $executor = new CachingExecutor($fallback, $cache); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableNever(), $this->expectCallableNever()); + } + + public function testQueryWillReturnResolvedPromiseWhenCacheReturnsHitWithoutSendingQueryToFallbackExecutor() + { + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->never())->method('query'); + + $message = new Message(); + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn(\React\Promise\resolve($message)); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableOnceWith($message), $this->expectCallableNever()); + } + + public function testQueryWillReturnResolvedPromiseWhenCacheReturnsMissAndFallbackExecutorResolvesAndSaveMessageToCacheWithMinimumTtlFromRecord() + { + $message = new Message(); + $message->answers[] = new Record('reactphp.org', Message::TYPE_A, Message::CLASS_IN, 3700, '127.0.0.1'); + $message->answers[] = new Record('reactphp.org', Message::TYPE_A, Message::CLASS_IN, 3600, '127.0.0.1'); + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->once())->method('query')->willReturn(\React\Promise\resolve($message)); + + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn(\React\Promise\resolve(null)); + $cache->expects($this->once())->method('set')->with('reactphp.org:1:1', $message, 3600); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableOnceWith($message), $this->expectCallableNever()); + } + + public function testQueryWillReturnResolvedPromiseWhenCacheReturnsMissAndFallbackExecutorResolvesAndSaveMessageToCacheWithDefaultTtl() + { + $message = new Message(); + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->once())->method('query')->willReturn(\React\Promise\resolve($message)); + + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn(\React\Promise\resolve(null)); + $cache->expects($this->once())->method('set')->with('reactphp.org:1:1', $message, 60); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableOnceWith($message), $this->expectCallableNever()); + } + + public function testQueryWillReturnResolvedPromiseWhenCacheReturnsMissAndFallbackExecutorResolvesWithTruncatedResponseButShouldNotSaveTruncatedMessageToCache() + { + $message = new Message(); + $message->tc = true; + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->once())->method('query')->willReturn(\React\Promise\resolve($message)); + + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn(\React\Promise\resolve(null)); + $cache->expects($this->never())->method('set'); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableOnceWith($message), $this->expectCallableNever()); + } + + public function testQueryWillReturnRejectedPromiseWhenCacheReturnsMissAndFallbackExecutorRejects() + { + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->once())->method('query')->willReturn(\React\Promise\reject(new \RuntimeException())); + + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->willReturn(\React\Promise\resolve(null)); + + $executor = new CachingExecutor($fallback, $cache); + + $promise = $executor->query($query); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnceWith($this->isInstanceOf('RuntimeException'))); + } + + public function testCancelQueryWillReturnRejectedPromiseAndCancelPendingPromiseFromCache() + { + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->never())->method('query'); + + $pending = new Promise(function () { }, $this->expectCallableOnce()); + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn($pending); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + $promise->cancel(); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnceWith($this->isInstanceOf('RuntimeException'))); + } + + public function testCancelQueryWillReturnRejectedPromiseAndCancelPendingPromiseFromFallbackExecutorWhenCacheReturnsMiss() + { + $pending = new Promise(function () { }, $this->expectCallableOnce()); + $fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $fallback->expects($this->once())->method('query')->willReturn($pending); + + $deferred = new Deferred(); + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $cache->expects($this->once())->method('get')->with('reactphp.org:1:1')->willReturn($deferred->promise()); + + $executor = new CachingExecutor($fallback, $cache); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + $deferred->resolve(null); + $promise->cancel(); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnceWith($this->isInstanceOf('RuntimeException'))); + } +} diff --git a/vendor/react/dns/tests/Query/CoopExecutorTest.php b/vendor/react/dns/tests/Query/CoopExecutorTest.php new file mode 100644 index 0000000..3e5074e --- /dev/null +++ b/vendor/react/dns/tests/Query/CoopExecutorTest.php @@ -0,0 +1,233 @@ +getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->with($query)->willReturn($pending); + $connector = new CoopExecutor($base); + + $connector->query($query); + } + + public function testQueryOnceWillResolveWhenBaseExecutorResolves() + { + $message = new Message(); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn(\React\Promise\resolve($message)); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $promise = $connector->query($query); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + + $promise->then($this->expectCallableOnceWith($message)); + } + + public function testQueryOnceWillRejectWhenBaseExecutorRejects() + { + $exception = new RuntimeException(); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn(\React\Promise\reject($exception)); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $promise = $connector->query($query); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + + $promise->then(null, $this->expectCallableOnceWith($exception)); + } + + public function testQueryTwoDifferentQueriesWillPassExactQueryToBaseExecutorTwice() + { + $pending = new Promise(function () { }); + $query1 = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $query2 = new Query('reactphp.org', Message::TYPE_AAAA, Message::CLASS_IN); + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->exactly(2))->method('query')->withConsecutive( + array($query1), + array($query2) + )->willReturn($pending); + $connector = new CoopExecutor($base); + + $connector->query($query1); + $connector->query($query2); + } + + public function testQueryTwiceWillPassExactQueryToBaseExecutorOnceWhenQueryIsStillPending() + { + $pending = new Promise(function () { }); + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->with($query)->willReturn($pending); + $connector = new CoopExecutor($base); + + $connector->query($query); + $connector->query($query); + } + + public function testQueryTwiceWillPassExactQueryToBaseExecutorTwiceWhenFirstQueryIsAlreadyResolved() + { + $deferred = new Deferred(); + $pending = new Promise(function () { }); + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->exactly(2))->method('query')->with($query)->willReturnOnConsecutiveCalls($deferred->promise(), $pending); + + $connector = new CoopExecutor($base); + + $connector->query($query); + + $deferred->resolve(new Message()); + + $connector->query($query); + } + + public function testQueryTwiceWillPassExactQueryToBaseExecutorTwiceWhenFirstQueryIsAlreadyRejected() + { + $deferred = new Deferred(); + $pending = new Promise(function () { }); + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->exactly(2))->method('query')->with($query)->willReturnOnConsecutiveCalls($deferred->promise(), $pending); + + $connector = new CoopExecutor($base); + + $connector->query($query); + + $deferred->reject(new RuntimeException()); + + $connector->query($query); + } + + public function testCancelQueryWillCancelPromiseFromBaseExecutorAndReject() + { + $promise = new Promise(function () { }, $this->expectCallableOnce()); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn($promise); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $promise = $connector->query($query); + + $promise->cancel(); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testCancelOneQueryWhenOtherQueryIsStillPendingWillNotCancelPromiseFromBaseExecutorAndRejectCancelled() + { + $promise = new Promise(function () { }, $this->expectCallableNever()); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn($promise); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $promise1 = $connector->query($query); + $promise2 = $connector->query($query); + + $promise1->cancel(); + + $promise1->then(null, $this->expectCallableOnce()); + $promise2->then(null, $this->expectCallableNever()); + } + + public function testCancelSecondQueryWhenFirstQueryIsStillPendingWillNotCancelPromiseFromBaseExecutorAndRejectCancelled() + { + $promise = new Promise(function () { }, $this->expectCallableNever()); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn($promise); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $promise1 = $connector->query($query); + $promise2 = $connector->query($query); + + $promise2->cancel(); + + $promise2->then(null, $this->expectCallableOnce()); + $promise1->then(null, $this->expectCallableNever()); + } + + public function testCancelAllPendingQueriesWillCancelPromiseFromBaseExecutorAndRejectCancelled() + { + $promise = new Promise(function () { }, $this->expectCallableOnce()); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn($promise); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + $promise1 = $connector->query($query); + $promise2 = $connector->query($query); + + $promise1->cancel(); + $promise2->cancel(); + + $promise1->then(null, $this->expectCallableOnce()); + $promise2->then(null, $this->expectCallableOnce()); + } + + public function testQueryTwiceWillQueryBaseExecutorTwiceIfFirstQueryHasAlreadyBeenCancelledWhenSecondIsStarted() + { + $promise = new Promise(function () { }, $this->expectCallableOnce()); + $pending = new Promise(function () { }); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->exactly(2))->method('query')->willReturnOnConsecutiveCalls($promise, $pending); + $connector = new CoopExecutor($base); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise1 = $connector->query($query); + $promise1->cancel(); + + $promise2 = $connector->query($query); + + $promise1->then(null, $this->expectCallableOnce()); + + $promise2->then(null, $this->expectCallableNever()); + } + + public function testCancelQueryShouldNotCauseGarbageReferences() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $deferred = new Deferred(function () { + throw new \RuntimeException(); + }); + + $base = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $base->expects($this->once())->method('query')->willReturn($deferred->promise()); + $connector = new CoopExecutor($base); + + gc_collect_cycles(); + + $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); + + $promise = $connector->query($query); + $promise->cancel(); + $promise = null; + + $this->assertEquals(0, gc_collect_cycles()); + } +} diff --git a/vendor/react/dns/tests/Query/ExecutorTest.php b/vendor/react/dns/tests/Query/ExecutorTest.php deleted file mode 100644 index 0d7ac1d..0000000 --- a/vendor/react/dns/tests/Query/ExecutorTest.php +++ /dev/null @@ -1,308 +0,0 @@ -loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $this->parser = $this->getMockBuilder('React\Dns\Protocol\Parser')->getMock(); - $this->dumper = new BinaryDumper(); - - $this->executor = new Executor($this->loop, $this->parser, $this->dumper); - } - - /** @test */ - public function queryShouldCreateUdpRequest() - { - $timer = $this->createTimerMock(); - $this->loop - ->expects($this->any()) - ->method('addTimer') - ->will($this->returnValue($timer)); - - $this->executor = $this->createExecutorMock(); - $this->executor - ->expects($this->once()) - ->method('createConnection') - ->with('8.8.8.8:53', 'udp') - ->will($this->returnNewConnectionMock(false)); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $this->executor->query('8.8.8.8:53', $query); - } - - /** @test */ - public function resolveShouldRejectIfRequestIsLargerThan512Bytes() - { - $query = new Query(str_repeat('a', 512).'.igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); - - $this->setExpectedException('RuntimeException', 'DNS query for ' . $query->name . ' failed: Requested transport "tcp" not available, only UDP is supported in this version'); - Block\await($promise, $this->loop); - } - - /** @test */ - public function resolveShouldCloseConnectionWhenCancelled() - { - $conn = $this->createConnectionMock(false); - $conn->expects($this->once())->method('close'); - - $timer = $this->createTimerMock(); - $this->loop - ->expects($this->any()) - ->method('addTimer') - ->will($this->returnValue($timer)); - - $this->executor = $this->createExecutorMock(); - $this->executor - ->expects($this->once()) - ->method('createConnection') - ->with('8.8.8.8:53', 'udp') - ->will($this->returnValue($conn)); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); - - $promise->cancel(); - - $this->setExpectedException('React\Dns\Query\CancellationException', 'DNS query for igor.io has been cancelled'); - Block\await($promise, $this->loop); - } - - /** @test */ - public function resolveShouldNotStartOrCancelTimerWhenCancelledWithTimeoutIsNull() - { - $this->loop - ->expects($this->never()) - ->method('addTimer'); - - $this->executor = new Executor($this->loop, $this->parser, $this->dumper, null); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('127.0.0.1:53', $query); - - $promise->cancel(); - - $this->setExpectedException('React\Dns\Query\CancellationException', 'DNS query for igor.io has been cancelled'); - Block\await($promise, $this->loop); - } - - /** @test */ - public function resolveShouldRejectIfResponseIsTruncated() - { - $timer = $this->createTimerMock(); - - $this->loop - ->expects($this->any()) - ->method('addTimer') - ->will($this->returnValue($timer)); - - $this->parser - ->expects($this->once()) - ->method('parseMessage') - ->will($this->returnTruncatedResponse()); - - $this->executor = $this->createExecutorMock(); - $this->executor - ->expects($this->once()) - ->method('createConnection') - ->with('8.8.8.8:53', 'udp') - ->will($this->returnNewConnectionMock()); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $this->executor->query('8.8.8.8:53', $query); - } - - /** @test */ - public function resolveShouldFailIfUdpThrow() - { - $this->loop - ->expects($this->never()) - ->method('addTimer'); - - $this->parser - ->expects($this->never()) - ->method('parseMessage'); - - $this->executor = $this->createExecutorMock(); - $this->executor - ->expects($this->once()) - ->method('createConnection') - ->with('8.8.8.8:53', 'udp') - ->will($this->throwException(new \Exception('Nope'))); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); - - $this->setExpectedException('RuntimeException', 'DNS query for igor.io failed: Nope'); - Block\await($promise, $this->loop); - } - - /** @test */ - public function resolveShouldCancelTimerWhenFullResponseIsReceived() - { - $conn = $this->createConnectionMock(); - - $this->parser - ->expects($this->once()) - ->method('parseMessage') - ->will($this->returnStandardResponse()); - - $this->executor = $this->createExecutorMock(); - $this->executor - ->expects($this->at(0)) - ->method('createConnection') - ->with('8.8.8.8:53', 'udp') - ->will($this->returnNewConnectionMock()); - - - $timer = $this->createTimerMock(); - - $this->loop - ->expects($this->once()) - ->method('addTimer') - ->with(5, $this->isInstanceOf('Closure')) - ->will($this->returnValue($timer)); - - $this->loop - ->expects($this->once()) - ->method('cancelTimer') - ->with($timer); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $this->executor->query('8.8.8.8:53', $query); - } - - /** @test */ - public function resolveShouldCloseConnectionOnTimeout() - { - $this->executor = $this->createExecutorMock(); - $this->executor - ->expects($this->at(0)) - ->method('createConnection') - ->with('8.8.8.8:53', 'udp') - ->will($this->returnNewConnectionMock(false)); - - $timer = $this->createTimerMock(); - - $this->loop - ->expects($this->never()) - ->method('cancelTimer'); - - $this->loop - ->expects($this->once()) - ->method('addTimer') - ->with(5, $this->isInstanceOf('Closure')) - ->will($this->returnCallback(function ($time, $callback) use (&$timerCallback, $timer) { - $timerCallback = $callback; - return $timer; - })); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); - - $this->assertNotNull($timerCallback); - $timerCallback(); - - $this->setExpectedException('React\Dns\Query\TimeoutException', 'DNS query for igor.io timed out'); - Block\await($promise, $this->loop); - } - - private function returnStandardResponse() - { - $that = $this; - $callback = function ($data) use ($that) { - $response = new Message(); - $that->convertMessageToStandardResponse($response); - return $response; - }; - - return $this->returnCallback($callback); - } - - private function returnTruncatedResponse() - { - $that = $this; - $callback = function ($data) use ($that) { - $response = new Message(); - $that->convertMessageToTruncatedResponse($response); - return $response; - }; - - return $this->returnCallback($callback); - } - - public function convertMessageToStandardResponse(Message $response) - { - $response->header->set('qr', 1); - $response->questions[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN); - $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'); - $response->prepare(); - - return $response; - } - - public function convertMessageToTruncatedResponse(Message $response) - { - $this->convertMessageToStandardResponse($response); - $response->header->set('tc', 1); - $response->prepare(); - - return $response; - } - - private function returnNewConnectionMock($emitData = true) - { - $conn = $this->createConnectionMock($emitData); - - $callback = function () use ($conn) { - return $conn; - }; - - return $this->returnCallback($callback); - } - - private function createConnectionMock($emitData = true) - { - $conn = $this->getMockBuilder('React\Stream\DuplexStreamInterface')->getMock(); - $conn - ->expects($this->any()) - ->method('on') - ->with('data', $this->isInstanceOf('Closure')) - ->will($this->returnCallback(function ($name, $callback) use ($emitData) { - $emitData && $callback(null); - })); - - return $conn; - } - - private function createTimerMock() - { - return $this->getMockBuilder( - interface_exists('React\EventLoop\TimerInterface') ? 'React\EventLoop\TimerInterface' : 'React\EventLoop\Timer\TimerInterface' - )->getMock(); - } - - private function createExecutorMock() - { - return $this->getMockBuilder('React\Dns\Query\Executor') - ->setConstructorArgs(array($this->loop, $this->parser, $this->dumper)) - ->setMethods(array('createConnection')) - ->getMock(); - } -} diff --git a/vendor/react/dns/tests/Query/HostsFileExecutorTest.php b/vendor/react/dns/tests/Query/HostsFileExecutorTest.php index 70d877e..455bafe 100644 --- a/vendor/react/dns/tests/Query/HostsFileExecutorTest.php +++ b/vendor/react/dns/tests/Query/HostsFileExecutorTest.php @@ -25,7 +25,7 @@ public function testDoesNotTryToGetIpsForMxQuery() $this->hosts->expects($this->never())->method('getIpsForHost'); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_MX, Message::CLASS_IN, 0)); + $this->executor->query(new Query('google.com', Message::TYPE_MX, Message::CLASS_IN)); } public function testFallsBackIfNoIpsWereFound() @@ -33,7 +33,7 @@ public function testFallsBackIfNoIpsWereFound() $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array()); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0)); + $this->executor->query(new Query('google.com', Message::TYPE_A, Message::CLASS_IN)); } public function testReturnsResponseMessageIfIpsWereFound() @@ -41,7 +41,7 @@ public function testReturnsResponseMessageIfIpsWereFound() $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('127.0.0.1')); $this->fallback->expects($this->never())->method('query'); - $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0)); + $ret = $this->executor->query(new Query('google.com', Message::TYPE_A, Message::CLASS_IN)); } public function testFallsBackIfNoIpv4Matches() @@ -49,7 +49,7 @@ public function testFallsBackIfNoIpv4Matches() $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('::1')); $this->fallback->expects($this->once())->method('query'); - $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0)); + $ret = $this->executor->query(new Query('google.com', Message::TYPE_A, Message::CLASS_IN)); } public function testReturnsResponseMessageIfIpv6AddressesWereFound() @@ -57,7 +57,7 @@ public function testReturnsResponseMessageIfIpv6AddressesWereFound() $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('::1')); $this->fallback->expects($this->never())->method('query'); - $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN, 0)); + $ret = $this->executor->query(new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN)); } public function testFallsBackIfNoIpv6Matches() @@ -65,7 +65,7 @@ public function testFallsBackIfNoIpv6Matches() $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('127.0.0.1')); $this->fallback->expects($this->once())->method('query'); - $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN, 0)); + $ret = $this->executor->query(new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN)); } public function testDoesReturnReverseIpv4Lookup() @@ -73,7 +73,7 @@ public function testDoesReturnReverseIpv4Lookup() $this->hosts->expects($this->once())->method('getHostsForIp')->with('127.0.0.1')->willReturn(array('localhost')); $this->fallback->expects($this->never())->method('query'); - $this->executor->query('8.8.8.8', new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN)); } public function testFallsBackIfNoReverseIpv4Matches() @@ -81,7 +81,7 @@ public function testFallsBackIfNoReverseIpv4Matches() $this->hosts->expects($this->once())->method('getHostsForIp')->with('127.0.0.1')->willReturn(array()); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN)); } public function testDoesReturnReverseIpv6Lookup() @@ -89,7 +89,7 @@ public function testDoesReturnReverseIpv6Lookup() $this->hosts->expects($this->once())->method('getHostsForIp')->with('2a02:2e0:3fe:100::6')->willReturn(array('ip6-localhost')); $this->fallback->expects($this->never())->method('query'); - $this->executor->query('8.8.8.8', new Query('6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.e.f.3.0.0.e.2.0.2.0.a.2.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.e.f.3.0.0.e.2.0.2.0.a.2.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN)); } public function testFallsBackForInvalidAddress() @@ -97,7 +97,7 @@ public function testFallsBackForInvalidAddress() $this->hosts->expects($this->never())->method('getHostsForIp'); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('example.com', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('example.com', Message::TYPE_PTR, Message::CLASS_IN)); } public function testReverseFallsBackForInvalidIpv4Address() @@ -105,7 +105,7 @@ public function testReverseFallsBackForInvalidIpv4Address() $this->hosts->expects($this->never())->method('getHostsForIp'); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('::1.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('::1.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN)); } public function testReverseFallsBackForInvalidLengthIpv6Address() @@ -113,7 +113,7 @@ public function testReverseFallsBackForInvalidLengthIpv6Address() $this->hosts->expects($this->never())->method('getHostsForIp'); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('abcd.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('abcd.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN)); } public function testReverseFallsBackForInvalidHexIpv6Address() @@ -121,6 +121,6 @@ public function testReverseFallsBackForInvalidHexIpv6Address() $this->hosts->expects($this->never())->method('getHostsForIp'); $this->fallback->expects($this->once())->method('query'); - $this->executor->query('8.8.8.8', new Query('zZz.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + $this->executor->query(new Query('zZz.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN)); } } diff --git a/vendor/react/dns/tests/Query/RecordBagTest.php b/vendor/react/dns/tests/Query/RecordBagTest.php deleted file mode 100644 index c0615be..0000000 --- a/vendor/react/dns/tests/Query/RecordBagTest.php +++ /dev/null @@ -1,83 +0,0 @@ -assertSame(array(), $recordBag->all()); - } - - /** - * @covers React\Dns\Query\RecordBag - * @test - */ - public function setShouldSetTheValue() - { - $currentTime = 1345656451; - - $recordBag = new RecordBag(); - $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600)); - - $records = $recordBag->all(); - $this->assertCount(1, $records); - $this->assertSame('igor.io', $records[0]->name); - $this->assertSame(Message::TYPE_A, $records[0]->type); - $this->assertSame(Message::CLASS_IN, $records[0]->class); - } - - /** - * @covers React\Dns\Query\RecordBag - * @test - */ - public function setShouldAcceptMxRecord() - { - $currentTime = 1345656451; - - $recordBag = new RecordBag(); - $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_MX, Message::CLASS_IN, 3600, array('priority' => 10, 'target' => 'igor.io'))); - - $records = $recordBag->all(); - $this->assertCount(1, $records); - $this->assertSame('igor.io', $records[0]->name); - $this->assertSame(Message::TYPE_MX, $records[0]->type); - $this->assertSame(Message::CLASS_IN, $records[0]->class); - $this->assertSame(array('priority' => 10, 'target' => 'igor.io'), $records[0]->data); - } - - /** - * @covers React\Dns\Query\RecordBag - * @test - */ - public function setShouldSetManyValues() - { - $currentTime = 1345656451; - - $recordBag = new RecordBag(); - $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132')); - - $records = $recordBag->all(); - $this->assertCount(2, $records); - $this->assertSame('igor.io', $records[0]->name); - $this->assertSame(Message::TYPE_A, $records[0]->type); - $this->assertSame(Message::CLASS_IN, $records[0]->class); - $this->assertSame('178.79.169.131', $records[0]->data); - $this->assertSame('igor.io', $records[1]->name); - $this->assertSame(Message::TYPE_A, $records[1]->type); - $this->assertSame(Message::CLASS_IN, $records[1]->class); - $this->assertSame('178.79.169.132', $records[1]->data); - } -} diff --git a/vendor/react/dns/tests/Query/RecordCacheTest.php b/vendor/react/dns/tests/Query/RecordCacheTest.php deleted file mode 100644 index 263db83..0000000 --- a/vendor/react/dns/tests/Query/RecordCacheTest.php +++ /dev/null @@ -1,193 +0,0 @@ -getMockBuilder('React\Cache\CacheInterface')->getMock(); - $base->expects($this->once())->method('get')->willReturn(\React\Promise\resolve(null)); - - $cache = new RecordCache($base); - $promise = $cache->lookup($query); - - $this->assertInstanceOf('React\Promise\RejectedPromise', $promise); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function lookupOnLegacyCacheMissShouldReturnNull() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - - $base = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); - $base->expects($this->once())->method('get')->willReturn(\React\Promise\reject()); - - $cache = new RecordCache($base); - $promise = $cache->lookup($query); - - $this->assertInstanceOf('React\Promise\RejectedPromise', $promise); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function storeRecordPendingCacheDoesNotSetCache() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $pending = new Promise(function () { }); - - $base = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); - $base->expects($this->once())->method('get')->willReturn($pending); - $base->expects($this->never())->method('set'); - - $cache = new RecordCache($base); - $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function storeRecordOnNewCacheMissSetsCache() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - - $base = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); - $base->expects($this->once())->method('get')->willReturn(\React\Promise\resolve(null)); - $base->expects($this->once())->method('set')->with($this->isType('string'), $this->isType('string')); - - $cache = new RecordCache($base); - $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function storeRecordOnOldCacheMissSetsCache() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - - $base = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); - $base->expects($this->once())->method('get')->willReturn(\React\Promise\reject()); - $base->expects($this->once())->method('set')->with($this->isType('string'), $this->isType('string')); - - $cache = new RecordCache($base); - $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function storeRecordShouldMakeLookupSucceed() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - - $cache = new RecordCache(new ArrayCache()); - $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - $promise = $cache->lookup($query); - - $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise); - $cachedRecords = $this->getPromiseValue($promise); - - $this->assertCount(1, $cachedRecords); - $this->assertSame('178.79.169.131', $cachedRecords[0]->data); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function storeTwoRecordsShouldReturnBoth() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - - $cache = new RecordCache(new ArrayCache()); - $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132')); - $promise = $cache->lookup($query); - - $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise); - $cachedRecords = $this->getPromiseValue($promise); - - $this->assertCount(2, $cachedRecords); - $this->assertSame('178.79.169.131', $cachedRecords[0]->data); - $this->assertSame('178.79.169.132', $cachedRecords[1]->data); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function storeResponseMessageShouldStoreAllAnswerValues() - { - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - - $response = new Message(); - $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'); - $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'); - $response->prepare(); - - $cache = new RecordCache(new ArrayCache()); - $cache->storeResponseMessage($query->currentTime, $response); - $promise = $cache->lookup($query); - - $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise); - $cachedRecords = $this->getPromiseValue($promise); - - $this->assertCount(2, $cachedRecords); - $this->assertSame('178.79.169.131', $cachedRecords[0]->data); - $this->assertSame('178.79.169.132', $cachedRecords[1]->data); - } - - /** - * @covers React\Dns\Query\RecordCache - * @test - */ - public function expireShouldExpireDeadRecords() - { - $cachedTime = 1345656451; - $currentTime = $cachedTime + 3605; - - $cache = new RecordCache(new ArrayCache()); - $cache->storeRecord($cachedTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); - $cache->expire($currentTime); - - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, $currentTime); - $promise = $cache->lookup($query); - - $this->assertInstanceOf('React\Promise\RejectedPromise', $promise); - } - - private function getPromiseValue(PromiseInterface $promise) - { - $capturedValue = null; - - $promise->then(function ($value) use (&$capturedValue) { - $capturedValue = $value; - }); - - return $capturedValue; - } -} diff --git a/vendor/react/dns/tests/Query/RetryExecutorTest.php b/vendor/react/dns/tests/Query/RetryExecutorTest.php index 7e44a08..f15f64c 100644 --- a/vendor/react/dns/tests/Query/RetryExecutorTest.php +++ b/vendor/react/dns/tests/Query/RetryExecutorTest.php @@ -24,13 +24,13 @@ public function queryShouldDelegateToDecoratedExecutor() $executor ->expects($this->once()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->with($this->isInstanceOf('React\Dns\Query\Query')) ->will($this->returnValue($this->expectPromiseOnce())); $retryExecutor = new RetryExecutor($executor, 2); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query); } /** @@ -45,12 +45,12 @@ public function queryShouldRetryQueryOnTimeout() $executor ->expects($this->exactly(2)) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->with($this->isInstanceOf('React\Dns\Query\Query')) ->will($this->onConsecutiveCalls( - $this->returnCallback(function ($domain, $query) { + $this->returnCallback(function ($query) { return Promise\reject(new TimeoutException("timeout")); }), - $this->returnCallback(function ($domain, $query) use ($response) { + $this->returnCallback(function ($query) use ($response) { return Promise\resolve($response); }) )); @@ -65,8 +65,8 @@ public function queryShouldRetryQueryOnTimeout() $retryExecutor = new RetryExecutor($executor, 2); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query)->then($callback, $errorback); } /** @@ -79,8 +79,8 @@ public function queryShouldStopRetryingAfterSomeAttempts() $executor ->expects($this->exactly(3)) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($domain, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { return Promise\reject(new TimeoutException("timeout")); })); @@ -94,8 +94,8 @@ public function queryShouldStopRetryingAfterSomeAttempts() $retryExecutor = new RetryExecutor($executor, 2); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query)->then($callback, $errorback); } /** @@ -108,8 +108,8 @@ public function queryShouldForwardNonTimeoutErrors() $executor ->expects($this->once()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($domain, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { return Promise\reject(new \Exception); })); @@ -123,8 +123,8 @@ public function queryShouldForwardNonTimeoutErrors() $retryExecutor = new RetryExecutor($executor, 2); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query)->then($callback, $errorback); } /** @@ -139,8 +139,8 @@ public function queryShouldCancelQueryOnCancel() $executor ->expects($this->once()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) use (&$cancelled) { $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { ++$cancelled; $reject(new CancellationException('Cancelled')); @@ -152,8 +152,8 @@ public function queryShouldCancelQueryOnCancel() $retryExecutor = new RetryExecutor($executor, 2); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $promise = $retryExecutor->query($query); $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); @@ -175,10 +175,10 @@ public function queryShouldCancelSecondQueryOnCancel() $executor ->expects($this->exactly(2)) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->with($this->isInstanceOf('React\Dns\Query\Query')) ->will($this->onConsecutiveCalls( $this->returnValue($deferred->promise()), - $this->returnCallback(function ($domain, $query) use (&$cancelled) { + $this->returnCallback(function ($query) use (&$cancelled) { $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { ++$cancelled; $reject(new CancellationException('Cancelled')); @@ -190,8 +190,8 @@ public function queryShouldCancelSecondQueryOnCancel() $retryExecutor = new RetryExecutor($executor, 2); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $promise = $retryExecutor->query($query); $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); @@ -217,14 +217,14 @@ public function queryShouldNotCauseGarbageReferencesOnSuccess() $executor ->expects($this->once()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->with($this->isInstanceOf('React\Dns\Query\Query')) ->willReturn(Promise\resolve($this->createStandardResponse())); $retryExecutor = new RetryExecutor($executor, 0); gc_collect_cycles(); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query); $this->assertEquals(0, gc_collect_cycles()); } @@ -243,14 +243,14 @@ public function queryShouldNotCauseGarbageReferencesOnTimeoutErrors() $executor ->expects($this->any()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->with($this->isInstanceOf('React\Dns\Query\Query')) ->willReturn(Promise\reject(new TimeoutException("timeout"))); $retryExecutor = new RetryExecutor($executor, 0); gc_collect_cycles(); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query); $this->assertEquals(0, gc_collect_cycles()); } @@ -273,14 +273,14 @@ public function queryShouldNotCauseGarbageReferencesOnCancellation() $executor ->expects($this->once()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->with($this->isInstanceOf('React\Dns\Query\Query')) ->willReturn($deferred->promise()); $retryExecutor = new RetryExecutor($executor, 0); gc_collect_cycles(); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $promise = $retryExecutor->query($query); $promise->cancel(); $promise = null; @@ -301,16 +301,16 @@ public function queryShouldNotCauseGarbageReferencesOnNonTimeoutErrors() $executor ->expects($this->once()) ->method('query') - ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($domain, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { return Promise\reject(new \Exception); })); $retryExecutor = new RetryExecutor($executor, 2); gc_collect_cycles(); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $retryExecutor->query('8.8.8.8', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $retryExecutor->query($query); $this->assertEquals(0, gc_collect_cycles()); } @@ -339,10 +339,9 @@ protected function createPromiseMock() protected function createStandardResponse() { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN); + $response->qr = true; + $response->questions[] = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'); - $response->prepare(); return $response; } diff --git a/vendor/react/dns/tests/Query/SelectiveTransportExecutorTest.php b/vendor/react/dns/tests/Query/SelectiveTransportExecutorTest.php new file mode 100644 index 0000000..d9d22b2 --- /dev/null +++ b/vendor/react/dns/tests/Query/SelectiveTransportExecutorTest.php @@ -0,0 +1,220 @@ +datagram = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $this->stream = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + + $this->executor = new SelectiveTransportExecutor($this->datagram, $this->stream); + } + + public function testQueryResolvesWhenDatagramTransportResolvesWithoutUsingStreamTransport() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $response = new Message(); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\resolve($response)); + + $this->stream + ->expects($this->never()) + ->method('query'); + + $promise = $this->executor->query($query); + + $promise->then($this->expectCallableOnceWith($response)); + } + + public function testQueryResolvesWhenStreamTransportResolvesAfterDatagramTransportRejectsWithSizeError() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $response = new Message(); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90))); + + $this->stream + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\resolve($response)); + + $promise = $this->executor->query($query); + + $promise->then($this->expectCallableOnceWith($response)); + } + + public function testQueryRejectsWhenDatagramTransportRejectsWithRuntimeExceptionWithoutUsingStreamTransport() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\reject(new \RuntimeException())); + + $this->stream + ->expects($this->never()) + ->method('query'); + + $promise = $this->executor->query($query); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testQueryRejectsWhenStreamTransportRejectsAfterDatagramTransportRejectsWithSizeError() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90))); + + $this->stream + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\reject(new \RuntimeException())); + + $promise = $this->executor->query($query); + + $promise->then(null, $this->expectCallableOnce()); + } + + public function testCancelPromiseWillCancelPromiseFromDatagramExecutor() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(new Promise(function () {}, $this->expectCallableOnce())); + + $promise = $this->executor->query($query); + $promise->cancel(); + } + + public function testCancelPromiseWillCancelPromiseFromStreamExecutorWhenDatagramExecutorRejectedWithTruncatedResponse() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $deferred = new Deferred(); + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn($deferred->promise()); + + $this->stream + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(new Promise(function () {}, $this->expectCallableOnce())); + + $promise = $this->executor->query($query); + $deferred->reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90)); + $promise->cancel(); + } + + public function testCancelPromiseShouldNotCreateAnyGarbageReferences() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(new Promise(function () {}, function () { + throw new \RuntimeException('Cancelled'); + })); + + gc_collect_cycles(); + $promise = $this->executor->query($query); + $promise->cancel(); + unset($promise); + + $this->assertEquals(0, gc_collect_cycles()); + } + + public function testCancelPromiseAfterTruncatedResponseShouldNotCreateAnyGarbageReferences() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $deferred = new Deferred(); + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn($deferred->promise()); + + $this->stream + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(new Promise(function () {}, function () { + throw new \RuntimeException('Cancelled'); + })); + + gc_collect_cycles(); + $promise = $this->executor->query($query); + $deferred->reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90)); + $promise->cancel(); + unset($promise); + + $this->assertEquals(0, gc_collect_cycles()); + } + + public function testRejectedPromiseAfterTruncatedResponseShouldNotCreateAnyGarbageReferences() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + + $this->datagram + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90))); + + $this->stream + ->expects($this->once()) + ->method('query') + ->with($query) + ->willReturn(\React\Promise\reject(new \RuntimeException())); + + gc_collect_cycles(); + $promise = $this->executor->query($query); + unset($promise); + + $this->assertEquals(0, gc_collect_cycles()); + } +} diff --git a/vendor/react/dns/tests/Query/TcpTransportExecutorTest.php b/vendor/react/dns/tests/Query/TcpTransportExecutorTest.php new file mode 100644 index 0000000..f15866d --- /dev/null +++ b/vendor/react/dns/tests/Query/TcpTransportExecutorTest.php @@ -0,0 +1,811 @@ +getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $executor = new TcpTransportExecutor($input, $loop); + + $ref = new \ReflectionProperty($executor, 'nameserver'); + $ref->setAccessible(true); + $value = $ref->getValue($executor); + + $this->assertEquals($expected, $value); + } + + public static function provideDefaultPortProvider() + { + return array( + array( + '8.8.8.8', + '8.8.8.8:53' + ), + array( + '1.2.3.4:5', + '1.2.3.4:5' + ), + array( + 'tcp://1.2.3.4', + '1.2.3.4:53' + ), + array( + 'tcp://1.2.3.4:53', + '1.2.3.4:53' + ), + array( + '::1', + '[::1]:53' + ), + array( + '[::1]:53', + '[::1]:53' + ) + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorShouldThrowWhenNameserverAddressIsInvalid() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + new TcpTransportExecutor('///', $loop); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorShouldThrowWhenNameserverAddressContainsHostname() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + new TcpTransportExecutor('localhost', $loop); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorShouldThrowWhenNameserverSchemeIsInvalid() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + new TcpTransportExecutor('udp://1.2.3.4', $loop); + } + + public function testQueryRejectsIfMessageExceedsMaximumMessageSize() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->never())->method('addWriteStream'); + + $executor = new TcpTransportExecutor('8.8.8.8:53', $loop); + + $query = new Query('google.' . str_repeat('.com', 60000), Message::TYPE_A, Message::CLASS_IN); + $promise = $executor->query($query); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + $promise->then(null, $this->expectCallableOnce()); + } + + public function testQueryRejectsIfServerConnectionFails() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->never())->method('addWriteStream'); + + $executor = new TcpTransportExecutor('::1', $loop); + + $ref = new \ReflectionProperty($executor, 'nameserver'); + $ref->setAccessible(true); + $ref->setValue($executor, '///'); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + $promise = $executor->query($query); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + $promise->then(null, $this->expectCallableOnce()); + } + + public function testQueryRejectsOnCancellationWithoutClosingSocketButStartsIdleTimer() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->never())->method('removeWriteStream'); + $loop->expects($this->never())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->anything())->willReturn($timer); + $loop->expects($this->never())->method('cancelTimer'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + $promise = $executor->query($query); + $promise->cancel(); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + $promise->then(null, $this->expectCallableOnce()); + } + + public function testTriggerIdleTimerAfterQueryRejectedOnCancellationWillCloseSocket() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->never())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $timerCallback = null; + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->callback(function ($cb) use (&$timerCallback) { + $timerCallback = $cb; + return true; + }))->willReturn($timer); + $loop->expects($this->once())->method('cancelTimer')->with($timer); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + $promise = $executor->query($query); + $promise->cancel(); + + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + $promise->then(null, $this->expectCallableOnce()); + + // trigger idle timer + $this->assertNotNull($timerCallback); + $timerCallback(); + } + + public function testQueryRejectsOnCancellationWithoutClosingSocketAndWithoutStartingIdleTimerWhenOtherQueryIsStillPending() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->never())->method('removeWriteStream'); + $loop->expects($this->never())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $loop->expects($this->never())->method('addTimer'); + $loop->expects($this->never())->method('cancelTimer'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + $promise1 = $executor->query($query); + $promise2 = $executor->query($query); + $promise2->cancel(); + + $promise1->then($this->expectCallableNever(), $this->expectCallableNever()); + $promise2->then(null, $this->expectCallableOnce()); + } + + public function testQueryAgainAfterPreviousWasCancelledReusesExistingSocket() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->never())->method('removeWriteStream'); + $loop->expects($this->never())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + $promise = $executor->query($query); + $promise->cancel(); + + $executor->query($query); + } + + public function testQueryRejectsWhenServerIsNotListening() + { + $loop = Factory::create(); + + $executor = new TcpTransportExecutor('127.0.0.1:1', $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.01, $loop); + if ($wait) { + \Clue\React\Block\sleep(0.2, $loop); + } + + $this->assertFalse($wait); + } + + public function testQueryStaysPendingWhenClientCanNotSendExcessiveMessageInOneChunkWhenServerClosesSocket() + { + $writableCallback = null; + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream')->with($this->anything(), $this->callback(function ($cb) use (&$writableCallback) { + $writableCallback = $cb; + return true; + })); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->never())->method('removeWriteStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google' . str_repeat('.com', 10000), Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + $client = stream_socket_accept($server); + fclose($client); + + $executor->handleWritable(); + + $promise->then($this->expectCallableNever(), $this->expectCallableNever()); + + $ref = new \ReflectionProperty($executor, 'writePending'); + $ref->setAccessible(true); + $writePending = $ref->getValue($executor); + + $this->assertTrue($writePending); + } + + public function testQueryRejectsWhenServerClosesConnection() + { + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop) { + $client = stream_socket_accept($server); + fclose($client); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.01, $loop); + if ($wait) { + \Clue\React\Block\sleep(0.2, $loop); + } + + $this->assertFalse($wait); + } + + public function testQueryKeepsPendingIfServerSendsIncompleteMessageLength() + { + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop) { + $client = stream_socket_accept($server); + $loop->addReadStream($client, function ($client) use ($loop) { + $loop->removeReadStream($client); + fwrite($client, "\x00"); + }); + + // keep reference to client to avoid disconnecting + $loop->addTimer(1, function () use ($client) { }); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.2, $loop); + $this->assertTrue($wait); + } + + public function testQueryKeepsPendingIfServerSendsIncompleteMessageBody() + { + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop) { + $client = stream_socket_accept($server); + $loop->addReadStream($client, function ($client) use ($loop) { + $loop->removeReadStream($client); + fwrite($client, "\x00\xff" . "some incomplete message data"); + }); + + // keep reference to client to avoid disconnecting + $loop->addTimer(1, function () use ($client) { }); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.2, $loop); + $this->assertTrue($wait); + } + + public function testQueryRejectsWhenServerSendsInvalidMessage() + { + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop) { + $client = stream_socket_accept($server); + $loop->addReadStream($client, function ($client) use ($loop) { + $loop->removeReadStream($client); + fwrite($client, "\x00\x0f" . 'invalid message'); + }); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.01, $loop); + if ($wait) { + \Clue\React\Block\sleep(0.2, $loop); + } + + $this->assertFalse($wait); + } + + public function testQueryRejectsWhenServerSendsInvalidId() + { + $parser = new Parser(); + $dumper = new BinaryDumper(); + + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop, $parser, $dumper) { + $client = stream_socket_accept($server); + $loop->addReadStream($client, function ($client) use ($loop, $parser, $dumper) { + $loop->removeReadStream($client); + $data = fread($client, 512); + + list(, $length) = unpack('n', substr($data, 0, 2)); + assert(strlen($data) - 2 === $length); + $data = substr($data, 2); + + $message = $parser->parseMessage($data); + $message->id = 0; + + $data = $dumper->toBinary($message); + $data = pack('n', strlen($data)) . $data; + + fwrite($client, $data); + }); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.01, $loop); + if ($wait) { + \Clue\React\Block\sleep(0.2, $loop); + } + + $this->assertFalse($wait); + } + + public function testQueryRejectsIfServerSendsTruncatedResponse() + { + $parser = new Parser(); + $dumper = new BinaryDumper(); + + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop, $parser, $dumper) { + $client = stream_socket_accept($server); + $loop->addReadStream($client, function ($client) use ($loop, $parser, $dumper) { + $loop->removeReadStream($client); + $data = fread($client, 512); + + list(, $length) = unpack('n', substr($data, 0, 2)); + assert(strlen($data) - 2 === $length); + $data = substr($data, 2); + + $message = $parser->parseMessage($data); + $message->tc = true; + + $data = $dumper->toBinary($message); + $data = pack('n', strlen($data)) . $data; + + fwrite($client, $data); + }); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $wait = true; + $executor->query($query)->then( + null, + function ($e) use (&$wait) { + $wait = false; + throw $e; + } + ); + + \Clue\React\Block\sleep(0.01, $loop); + if ($wait) { + \Clue\React\Block\sleep(0.2, $loop); + } + + $this->assertFalse($wait); + } + + public function testQueryResolvesIfServerSendsValidResponse() + { + $loop = Factory::create(); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $loop->addReadStream($server, function ($server) use ($loop) { + $client = stream_socket_accept($server); + $loop->addReadStream($client, function ($client) use ($loop) { + $loop->removeReadStream($client); + $data = fread($client, 512); + + list(, $length) = unpack('n', substr($data, 0, 2)); + assert(strlen($data) - 2 === $length); + + fwrite($client, $data); + }); + }); + + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + $response = \Clue\React\Block\await($promise, $loop, 0.2); + + $this->assertInstanceOf('React\Dns\Model\Message', $response); + } + + public function testQueryRejectsIfSocketIsClosedAfterPreviousQueryThatWasStillPending() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->exactly(2))->method('addWriteStream'); + $loop->expects($this->exactly(2))->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->once())->method('removeReadStream'); + + $loop->expects($this->never())->method('addTimer'); + $loop->expects($this->never())->method('cancelTimer'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise1 = $executor->query($query); + + $client = stream_socket_accept($server); + + $executor->handleWritable(); + + // manually close socket before processing second write + $ref = new \ReflectionProperty($executor, 'socket'); + $ref->setAccessible(true); + $socket = $ref->getValue($executor); + fclose($socket); + fclose($client); + + $promise2 = $executor->query($query); + + $executor->handleWritable(); + + $promise1->then(null, $this->expectCallableOnce()); + $promise2->then(null, $this->expectCallableOnce()); + } + + public function testQueryResolvesIfServerSendsBackResponseMessageAndWillStartIdleTimer() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->anything()); + $loop->expects($this->never())->method('cancelTimer'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + // use outgoing buffer as response message + $ref = new \ReflectionProperty($executor, 'writeBuffer'); + $ref->setAccessible(true); + $data = $ref->getValue($executor); + + $client = stream_socket_accept($server); + fwrite($client, $data); + + $executor->handleWritable(); + $executor->handleRead(); + + $promise->then($this->expectCallableOnce()); + } + + public function testQueryResolvesIfServerSendsBackResponseMessageAfterCancellingQueryAndWillStartIdleTimer() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->anything())->willReturn($timer); + $loop->expects($this->never())->method('cancelTimer'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + $promise->cancel(); + + // use outgoing buffer as response message + $ref = new \ReflectionProperty($executor, 'writeBuffer'); + $ref->setAccessible(true); + $data = $ref->getValue($executor); + + $client = stream_socket_accept($server); + fwrite($client, $data); + + $executor->handleWritable(); + $executor->handleRead(); + + //$promise->then(null, $this->expectCallableOnce()); + } + + public function testQueryResolvesIfServerSendsBackResponseMessageAfterCancellingOtherQueryAndWillStartIdleTimer() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->anything()); + $loop->expects($this->never())->method('cancelTimer'); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + // use outgoing buffer as response message + $ref = new \ReflectionProperty($executor, 'writeBuffer'); + $ref->setAccessible(true); + $data = $ref->getValue($executor); + + $client = stream_socket_accept($server); + fwrite($client, $data); + + $another = $executor->query($query); + $another->cancel(); + + $executor->handleWritable(); + $executor->handleRead(); + + $promise->then($this->expectCallableOnce()); + } + + public function testTriggerIdleTimerAfterPreviousQueryResolvedWillCloseIdleSocketConnection() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->once())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $timerCallback = null; + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->callback(function ($cb) use (&$timerCallback) { + $timerCallback = $cb; + return true; + }))->willReturn($timer); + $loop->expects($this->once())->method('cancelTimer')->with($timer); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + // use outgoing buffer as response message + $ref = new \ReflectionProperty($executor, 'writeBuffer'); + $ref->setAccessible(true); + $data = $ref->getValue($executor); + + $client = stream_socket_accept($server); + fwrite($client, $data); + + $executor->handleWritable(); + $executor->handleRead(); + + $promise->then($this->expectCallableOnce()); + + // trigger idle timer + $this->assertNotNull($timerCallback); + $timerCallback(); + } + + public function testClosingConnectionAfterPreviousQueryResolvedWillCancelIdleTimer() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->once())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->anything())->willReturn($timer); + $loop->expects($this->once())->method('cancelTimer')->with($timer); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + // use outgoing buffer as response message + $ref = new \ReflectionProperty($executor, 'writeBuffer'); + $ref->setAccessible(true); + $data = $ref->getValue($executor); + + $client = stream_socket_accept($server); + fwrite($client, $data); + + $executor->handleWritable(); + $executor->handleRead(); + + $promise->then($this->expectCallableOnce()); + + // trigger connection close condition + fclose($client); + $executor->handleRead(); + } + + public function testQueryAgainAfterPreviousQueryResolvedWillReuseSocketAndCancelIdleTimer() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->exactly(2))->method('addWriteStream'); + $loop->expects($this->once())->method('removeWriteStream'); + $loop->expects($this->once())->method('addReadStream'); + $loop->expects($this->never())->method('removeReadStream'); + + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + $loop->expects($this->once())->method('addTimer')->with(0.001, $this->anything())->willReturn($timer); + $loop->expects($this->once())->method('cancelTimer')->with($timer); + + $server = stream_socket_server('tcp://127.0.0.1:0'); + $address = stream_socket_get_name($server, false); + $executor = new TcpTransportExecutor($address, $loop); + + $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); + + $promise = $executor->query($query); + + // use outgoing buffer as response message + $ref = new \ReflectionProperty($executor, 'writeBuffer'); + $ref->setAccessible(true); + $data = $ref->getValue($executor); + + $client = stream_socket_accept($server); + fwrite($client, $data); + + $executor->handleWritable(); + $executor->handleRead(); + + $promise->then($this->expectCallableOnce()); + + // trigger second query + $executor->query($query); + } +} diff --git a/vendor/react/dns/tests/Query/TimeoutExecutorTest.php b/vendor/react/dns/tests/Query/TimeoutExecutorTest.php index 0d37fb4..1f2d30c 100644 --- a/vendor/react/dns/tests/Query/TimeoutExecutorTest.php +++ b/vendor/react/dns/tests/Query/TimeoutExecutorTest.php @@ -29,7 +29,7 @@ public function testCancellingPromiseWillCancelWrapped() $this->wrapped ->expects($this->once()) ->method('query') - ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) { + ->will($this->returnCallback(function ($query) use (&$cancelled) { $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { ++$cancelled; $reject(new CancellationException('Cancelled')); @@ -38,8 +38,8 @@ public function testCancellingPromiseWillCancelWrapped() return $deferred->promise(); })); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $promise = $this->executor->query($query); $this->assertEquals(0, $cancelled); $promise->cancel(); @@ -55,8 +55,8 @@ public function testResolvesPromiseWhenWrappedResolves() ->method('query') ->willReturn(Promise\resolve('0.0.0.0')); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $promise = $this->executor->query($query); $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); } @@ -68,8 +68,8 @@ public function testRejectsPromiseWhenWrappedRejects() ->method('query') ->willReturn(Promise\reject(new \RuntimeException())); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $promise = $this->executor->query('8.8.8.8:53', $query); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $promise = $this->executor->query($query); $promise->then($this->expectCallableNever(), $this->expectCallableOnceWith(new \RuntimeException())); } @@ -83,7 +83,7 @@ public function testWrappedWillBeCancelledOnTimeout() $this->wrapped ->expects($this->once()) ->method('query') - ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) { + ->will($this->returnCallback(function ($query) use (&$cancelled) { $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { ++$cancelled; $reject(new CancellationException('Cancelled')); @@ -103,8 +103,8 @@ public function testWrappedWillBeCancelledOnTimeout() $this->attribute($this->equalTo('DNS query for igor.io timed out'), 'message') )); - $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); - $this->executor->query('8.8.8.8:53', $query)->then($callback, $errorback); + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN); + $this->executor->query($query)->then($callback, $errorback); $this->assertEquals(0, $cancelled); diff --git a/vendor/react/dns/tests/Query/UdpTransportExecutorTest.php b/vendor/react/dns/tests/Query/UdpTransportExecutorTest.php index f7222dc..d01ab3e 100644 --- a/vendor/react/dns/tests/Query/UdpTransportExecutorTest.php +++ b/vendor/react/dns/tests/Query/UdpTransportExecutorTest.php @@ -12,21 +12,103 @@ class UdpTransportExecutorTest extends TestCase { + /** + * @dataProvider provideDefaultPortProvider + * @param string $input + * @param string $expected + */ + public function testCtorShouldAcceptNameserverAddresses($input, $expected) + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $executor = new UdpTransportExecutor($input, $loop); + + $ref = new \ReflectionProperty($executor, 'nameserver'); + $ref->setAccessible(true); + $value = $ref->getValue($executor); + + $this->assertEquals($expected, $value); + } + + public static function provideDefaultPortProvider() + { + return array( + array( + '8.8.8.8', + 'udp://8.8.8.8:53' + ), + array( + '1.2.3.4:5', + 'udp://1.2.3.4:5' + ), + array( + 'udp://1.2.3.4', + 'udp://1.2.3.4:53' + ), + array( + 'udp://1.2.3.4:53', + 'udp://1.2.3.4:53' + ), + array( + '::1', + 'udp://[::1]:53' + ), + array( + '[::1]:53', + 'udp://[::1]:53' + ) + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorShouldThrowWhenNameserverAddressIsInvalid() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + new UdpTransportExecutor('///', $loop); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorShouldThrowWhenNameserverAddressContainsHostname() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + new UdpTransportExecutor('localhost', $loop); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCtorShouldThrowWhenNameserverSchemeIsInvalid() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + new UdpTransportExecutor('tcp://1.2.3.4', $loop); + } + public function testQueryRejectsIfMessageExceedsUdpSize() { $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); $loop->expects($this->never())->method('addReadStream'); - $dumper = $this->getMockBuilder('React\Dns\Protocol\BinaryDumper')->getMock(); - $dumper->expects($this->once())->method('toBinary')->willReturn(str_repeat('.', 513)); + $executor = new UdpTransportExecutor('8.8.8.8:53', $loop); - $executor = new UdpTransportExecutor($loop, null, $dumper); - - $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); - $promise = $executor->query('8.8.8.8:53', $query); + $query = new Query('google.' . str_repeat('.com', 200), Message::TYPE_A, Message::CLASS_IN); + $promise = $executor->query($query); $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); - $promise->then(null, $this->expectCallableOnce()); + + $exception = null; + $promise->then(null, function ($reason) use (&$exception) { + $exception = $reason; + }); + + $this->setExpectedException('RuntimeException', '', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90); + throw $exception; } public function testQueryRejectsIfServerConnectionFails() @@ -34,10 +116,14 @@ public function testQueryRejectsIfServerConnectionFails() $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); $loop->expects($this->never())->method('addReadStream'); - $executor = new UdpTransportExecutor($loop); + $executor = new UdpTransportExecutor('::1', $loop); + + $ref = new \ReflectionProperty($executor, 'nameserver'); + $ref->setAccessible(true); + $ref->setValue($executor, '///'); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); - $promise = $executor->query('///', $query); + $promise = $executor->query($query); $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); $promise->then(null, $this->expectCallableOnce()); @@ -52,10 +138,10 @@ public function testQueryRejectsOnCancellation() $loop->expects($this->once())->method('addReadStream'); $loop->expects($this->once())->method('removeReadStream'); - $executor = new UdpTransportExecutor($loop); + $executor = new UdpTransportExecutor('8.8.8.8:53', $loop); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); - $promise = $executor->query('8.8.8.8:53', $query); + $promise = $executor->query($query); $promise->cancel(); $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); @@ -66,12 +152,12 @@ public function testQueryKeepsPendingIfServerRejectsNetworkPacket() { $loop = Factory::create(); - $executor = new UdpTransportExecutor($loop); + $executor = new UdpTransportExecutor('127.0.0.1:1', $loop); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); $wait = true; - $promise = $executor->query('127.0.0.1:1', $query)->then( + $promise = $executor->query($query)->then( null, function ($e) use (&$wait) { $wait = false; @@ -83,7 +169,7 @@ function ($e) use (&$wait) { $this->assertTrue($wait); } - public function testQueryKeepsPendingIfServerSendInvalidMessage() + public function testQueryKeepsPendingIfServerSendsInvalidMessage() { $loop = Factory::create(); @@ -94,12 +180,12 @@ public function testQueryKeepsPendingIfServerSendInvalidMessage() }); $address = stream_socket_get_name($server, false); - $executor = new UdpTransportExecutor($loop); + $executor = new UdpTransportExecutor($address, $loop); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); $wait = true; - $promise = $executor->query($address, $query)->then( + $promise = $executor->query($query)->then( null, function ($e) use (&$wait) { $wait = false; @@ -111,7 +197,7 @@ function ($e) use (&$wait) { $this->assertTrue($wait); } - public function testQueryKeepsPendingIfServerSendInvalidId() + public function testQueryKeepsPendingIfServerSendsInvalidId() { $parser = new Parser(); $dumper = new BinaryDumper(); @@ -123,18 +209,18 @@ public function testQueryKeepsPendingIfServerSendInvalidId() $data = stream_socket_recvfrom($server, 512, 0, $peer); $message = $parser->parseMessage($data); - $message->header->set('id', 0); + $message->id = 0; stream_socket_sendto($server, $dumper->toBinary($message), 0, $peer); }); $address = stream_socket_get_name($server, false); - $executor = new UdpTransportExecutor($loop, $parser, $dumper); + $executor = new UdpTransportExecutor($address, $loop); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); $wait = true; - $promise = $executor->query($address, $query)->then( + $promise = $executor->query($query)->then( null, function ($e) use (&$wait) { $wait = false; @@ -158,32 +244,20 @@ public function testQueryRejectsIfServerSendsTruncatedResponse() $data = stream_socket_recvfrom($server, 512, 0, $peer); $message = $parser->parseMessage($data); - $message->header->set('tc', 1); + $message->tc = true; stream_socket_sendto($server, $dumper->toBinary($message), 0, $peer); }); $address = stream_socket_get_name($server, false); - $executor = new UdpTransportExecutor($loop, $parser, $dumper); + $executor = new UdpTransportExecutor($address, $loop); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); - $wait = true; - $promise = $executor->query($address, $query)->then( - null, - function ($e) use (&$wait) { - $wait = false; - throw $e; - } - ); - - // run loop for short period to ensure we detect connection ICMP rejection error - \Clue\React\Block\sleep(0.01, $loop); - if ($wait) { - \Clue\React\Block\sleep(0.2, $loop); - } + $promise = $executor->query($query); - $this->assertFalse($wait); + $this->setExpectedException('RuntimeException', '', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90); + \Clue\React\Block\await($promise, $loop, 0.1); } public function testQueryResolvesIfServerSendsValidResponse() @@ -203,11 +277,11 @@ public function testQueryResolvesIfServerSendsValidResponse() }); $address = stream_socket_get_name($server, false); - $executor = new UdpTransportExecutor($loop, $parser, $dumper); + $executor = new UdpTransportExecutor($address, $loop); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); - $promise = $executor->query($address, $query); + $promise = $executor->query($query); $response = \Clue\React\Block\await($promise, $loop, 0.2); $this->assertInstanceOf('React\Dns\Model\Message', $response); diff --git a/vendor/react/dns/tests/Resolver/FactoryTest.php b/vendor/react/dns/tests/Resolver/FactoryTest.php index acaeac0..1f201bf 100644 --- a/vendor/react/dns/tests/Resolver/FactoryTest.php +++ b/vendor/react/dns/tests/Resolver/FactoryTest.php @@ -19,78 +19,163 @@ public function createShouldCreateResolver() $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); } + /** @test */ - public function createWithoutPortShouldCreateResolverWithDefaultPort() + public function createWithoutSchemeShouldCreateResolverWithSelectiveUdpAndTcpExecutorStack() { $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); $factory = new Factory(); - $resolver = $factory->create('8.8.8.8', $loop); + $resolver = $factory->create('8.8.8.8:53', $loop); $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); - $this->assertSame('8.8.8.8:53', $this->getResolverPrivateMemberValue($resolver, 'nameserver')); + + $coopExecutor = $this->getResolverPrivateExecutor($resolver); + + $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); + + $ref = new \ReflectionProperty($coopExecutor, 'executor'); + $ref->setAccessible(true); + $selectiveExecutor = $ref->getValue($coopExecutor); + + $this->assertInstanceOf('React\Dns\Query\SelectiveTransportExecutor', $selectiveExecutor); + + // udp below: + + $ref = new \ReflectionProperty($selectiveExecutor, 'datagramExecutor'); + $ref->setAccessible(true); + $retryExecutor = $ref->getValue($selectiveExecutor); + + $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); + + $ref = new \ReflectionProperty($retryExecutor, 'executor'); + $ref->setAccessible(true); + $timeoutExecutor = $ref->getValue($retryExecutor); + + $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); + + $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); + $ref->setAccessible(true); + $udpExecutor = $ref->getValue($timeoutExecutor); + + $this->assertInstanceOf('React\Dns\Query\UdpTransportExecutor', $udpExecutor); + + // tcp below: + + $ref = new \ReflectionProperty($selectiveExecutor, 'streamExecutor'); + $ref->setAccessible(true); + $timeoutExecutor = $ref->getValue($selectiveExecutor); + + $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); + + $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); + $ref->setAccessible(true); + $tcpExecutor = $ref->getValue($timeoutExecutor); + + $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); } /** @test */ - public function createCachedShouldCreateResolverWithCachedExecutor() + public function createWithUdpSchemeShouldCreateResolverWithUdpExecutorStack() { $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); $factory = new Factory(); - $resolver = $factory->createCached('8.8.8.8:53', $loop); + $resolver = $factory->create('udp://8.8.8.8:53', $loop); $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); - $executor = $this->getResolverPrivateExecutor($resolver); - $this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor); - $recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache'); - $recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache'); - $this->assertInstanceOf('React\Cache\CacheInterface', $recordCacheCache); - $this->assertInstanceOf('React\Cache\ArrayCache', $recordCacheCache); + + $coopExecutor = $this->getResolverPrivateExecutor($resolver); + + $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); + + $ref = new \ReflectionProperty($coopExecutor, 'executor'); + $ref->setAccessible(true); + $retryExecutor = $ref->getValue($coopExecutor); + + $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); + + $ref = new \ReflectionProperty($retryExecutor, 'executor'); + $ref->setAccessible(true); + $timeoutExecutor = $ref->getValue($retryExecutor); + + $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); + + $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); + $ref->setAccessible(true); + $udpExecutor = $ref->getValue($timeoutExecutor); + + $this->assertInstanceOf('React\Dns\Query\UdpTransportExecutor', $udpExecutor); } /** @test */ - public function createCachedShouldCreateResolverWithCachedExecutorWithCustomCache() + public function createWithTcpSchemeShouldCreateResolverWithTcpExecutorStack() { - $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); $factory = new Factory(); - $resolver = $factory->createCached('8.8.8.8:53', $loop, $cache); + $resolver = $factory->create('tcp://8.8.8.8:53', $loop); $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); - $executor = $this->getResolverPrivateExecutor($resolver); - $this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor); - $recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache'); - $recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache'); - $this->assertInstanceOf('React\Cache\CacheInterface', $recordCacheCache); - $this->assertSame($cache, $recordCacheCache); + + $coopExecutor = $this->getResolverPrivateExecutor($resolver); + + $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); + + $ref = new \ReflectionProperty($coopExecutor, 'executor'); + $ref->setAccessible(true); + $timeoutExecutor = $ref->getValue($coopExecutor); + + $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); + + $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); + $ref->setAccessible(true); + $tcpExecutor = $ref->getValue($timeoutExecutor); + + $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); } /** * @test - * @dataProvider factoryShouldAddDefaultPortProvider + * @expectedException InvalidArgumentException */ - public function factoryShouldAddDefaultPort($input, $expected) + public function createShouldThrowWhenNameserverIsInvalid() { $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); $factory = new Factory(); - $resolver = $factory->create($input, $loop); + $factory->create('///', $loop); + } + + /** @test */ + public function createCachedShouldCreateResolverWithCachingExecutor() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->createCached('8.8.8.8:53', $loop); $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); - $this->assertSame($expected, $this->getResolverPrivateMemberValue($resolver, 'nameserver')); + $executor = $this->getResolverPrivateExecutor($resolver); + $this->assertInstanceOf('React\Dns\Query\CachingExecutor', $executor); + $cache = $this->getCachingExecutorPrivateMemberValue($executor, 'cache'); + $this->assertInstanceOf('React\Cache\ArrayCache', $cache); } - public static function factoryShouldAddDefaultPortProvider() + /** @test */ + public function createCachedShouldCreateResolverWithCachingExecutorWithCustomCache() { - return array( - array('8.8.8.8', '8.8.8.8:53'), - array('1.2.3.4:5', '1.2.3.4:5'), - array('localhost', 'localhost:53'), - array('localhost:1234', 'localhost:1234'), - array('::1', '[::1]:53'), - array('[::1]:53', '[::1]:53') - ); + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->createCached('8.8.8.8:53', $loop, $cache); + + $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); + $executor = $this->getResolverPrivateExecutor($resolver); + $this->assertInstanceOf('React\Dns\Query\CachingExecutor', $executor); + $cacheProperty = $this->getCachingExecutorPrivateMemberValue($executor, 'cache'); + $this->assertSame($cache, $cacheProperty); } private function getResolverPrivateExecutor($resolver) @@ -115,16 +200,9 @@ private function getResolverPrivateMemberValue($resolver, $field) return $reflector->getValue($resolver); } - private function getCachedExecutorPrivateMemberValue($resolver, $field) - { - $reflector = new \ReflectionProperty('React\Dns\Query\CachedExecutor', $field); - $reflector->setAccessible(true); - return $reflector->getValue($resolver); - } - - private function getRecordCachePrivateMemberValue($resolver, $field) + private function getCachingExecutorPrivateMemberValue($resolver, $field) { - $reflector = new \ReflectionProperty('React\Dns\Query\RecordCache', $field); + $reflector = new \ReflectionProperty('React\Dns\Query\CachingExecutor', $field); $reflector->setAccessible(true); return $reflector->getValue($resolver); } diff --git a/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php b/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php index a9b8608..98901be 100644 --- a/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php +++ b/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php @@ -2,27 +2,31 @@ namespace React\Tests\Dns\Resolver; -use PHPUnit\Framework\TestCase; +use React\Tests\Dns\TestCase; use React\Dns\Resolver\Resolver; -use React\Dns\Query\Query; use React\Dns\Model\Message; use React\Dns\Model\Record; class ResolveAliasesTest extends TestCase { /** - * @covers React\Dns\Resolver\Resolver::resolveAliases - * @covers React\Dns\Resolver\Resolver::valuesByNameAndType * @dataProvider provideAliasedAnswers */ public function testResolveAliases(array $expectedAnswers, array $answers, $name) { + $message = new Message(); + foreach ($answers as $answer) { + $message->answers[] = $answer; + } + $executor = $this->createExecutorMock(); - $resolver = new Resolver('8.8.8.8:53', $executor); + $executor->expects($this->once())->method('query')->willReturn(\React\Promise\resolve($message)); + + $resolver = new Resolver($executor); - $answers = $resolver->resolveAliases($answers, $name); + $answers = $resolver->resolveAll($name, Message::TYPE_A); - $this->assertEquals($expectedAnswers, $answers); + $answers->then($this->expectCallableOnceWith($expectedAnswers), null); } public function provideAliasedAnswers() @@ -53,14 +57,6 @@ public function provideAliasedAnswers() ), 'igor.io', ), - array( - array(), - array( - new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN), - new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN), - ), - 'igor.io', - ), array( array('178.79.169.131'), array( diff --git a/vendor/react/dns/tests/Resolver/ResolverTest.php b/vendor/react/dns/tests/Resolver/ResolverTest.php index 661386d..da7429a 100644 --- a/vendor/react/dns/tests/Resolver/ResolverTest.php +++ b/vendor/react/dns/tests/Resolver/ResolverTest.php @@ -19,17 +19,17 @@ public function resolveShouldQueryARecords() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query($query->name, $query->type, $query->class); $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, '178.79.169.131'); return Promise\resolve($response); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolve('igor.io')->then($this->expectCallableOnceWith('178.79.169.131')); } @@ -40,17 +40,17 @@ public function resolveAllShouldQueryGivenRecords() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query($query->name, $query->type, $query->class); $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, '::1'); return Promise\resolve($response); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then($this->expectCallableOnceWith(array('::1'))); } @@ -61,18 +61,18 @@ public function resolveAllShouldIgnoreRecordsWithOtherTypes() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query($query->name, $query->type, $query->class); $response->answers[] = new Record($query->name, Message::TYPE_TXT, $query->class, 3600, array('ignored')); $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, '::1'); return Promise\resolve($response); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then($this->expectCallableOnceWith(array('::1'))); } @@ -83,20 +83,19 @@ public function resolveAllShouldReturnMultipleValuesForAlias() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query($query->name, $query->type, $query->class); $response->answers[] = new Record($query->name, Message::TYPE_CNAME, $query->class, 3600, 'example.com'); $response->answers[] = new Record('example.com', $query->type, $query->class, 3600, '::1'); $response->answers[] = new Record('example.com', $query->type, $query->class, 3600, '::2'); - $response->prepare(); return Promise\resolve($response); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then( $this->expectCallableOnceWith($this->equalTo(array('::1', '::2'))) ); @@ -109,17 +108,17 @@ public function resolveShouldQueryARecordsAndIgnoreCase() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record('Blog.wyrihaximus.net', $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query('Blog.wyrihaximus.net', $query->type, $query->class); $response->answers[] = new Record('Blog.wyrihaximus.net', $query->type, $query->class, 3600, '178.79.169.131'); return Promise\resolve($response); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolve('blog.wyrihaximus.net')->then($this->expectCallableOnceWith('178.79.169.131')); } @@ -130,11 +129,11 @@ public function resolveShouldFilterByName() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query($query->name, $query->type, $query->class); $response->answers[] = new Record('foo.bar', $query->type, $query->class, 3600, '178.79.169.131'); return Promise\resolve($response); @@ -142,7 +141,7 @@ public function resolveShouldFilterByName() $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException')); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback); } @@ -155,11 +154,11 @@ public function resolveWithNoAnswersShouldCallErrbackIfGiven() $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) { $response = new Message(); - $response->header->set('qr', 1); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->questions[] = new Query($query->name, $query->type, $query->class); return Promise\resolve($response); })); @@ -168,7 +167,7 @@ public function resolveWithNoAnswersShouldCallErrbackIfGiven() return ($param instanceof RecordNotFoundException && $param->getCode() === 0 && $param->getMessage() === 'DNS query for igor.io did not return a valid answer (NOERROR / NODATA)'); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback); } @@ -212,12 +211,12 @@ public function resolveWithRcodeErrorShouldCallErrbackIfGiven($code, $expectedMe $executor ->expects($this->once()) ->method('query') - ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) - ->will($this->returnCallback(function ($nameserver, $query) use ($code) { + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($query) use ($code) { $response = new Message(); - $response->header->set('qr', 1); - $response->header->set('rcode', $code); - $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->qr = true; + $response->rcode = $code; + $response->questions[] = new Query($query->name, $query->type, $query->class); return Promise\resolve($response); })); @@ -226,24 +225,10 @@ public function resolveWithRcodeErrorShouldCallErrbackIfGiven($code, $expectedMe return ($param instanceof RecordNotFoundException && $param->getCode() === $code && $param->getMessage() === $expectedMessage); })); - $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver = new Resolver($executor); $resolver->resolve('example.com')->then($this->expectCallableNever(), $errback); } - public function testLegacyExtractAddress() - { - $executor = $this->createExecutorMock(); - $resolver = new Resolver('8.8.8.8:53', $executor); - - $query = new Query('reactphp.org', Message::TYPE_A, Message::CLASS_IN); - $response = Message::createResponseWithAnswersForQuery($query, array( - new Record('reactphp.org', Message::TYPE_A, Message::CLASS_IN, 3600, '1.2.3.4') - )); - - $ret = $resolver->extractAddress($query, $response); - $this->assertEquals('1.2.3.4', $ret); - } - private function createExecutorMock() { return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); diff --git a/vendor/react/event-loop/.gitignore b/vendor/react/event-loop/.gitignore deleted file mode 100644 index 81b9258..0000000 --- a/vendor/react/event-loop/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -composer.lock -phpunit.xml -vendor diff --git a/vendor/react/event-loop/.travis.yml b/vendor/react/event-loop/.travis.yml deleted file mode 100644 index 7af713a..0000000 --- a/vendor/react/event-loop/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: php - -php: -# - 5.3 # requires old distro, see below - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - hhvm # ignore errors, see below - -# lock distro so new future defaults will not break the build -dist: trusty - -matrix: - include: - - php: 5.3 - dist: precise - allow_failures: - - php: hhvm - -sudo: false - -addons: - apt: - packages: - - libevent-dev # Used by 'event' and 'libevent' PHP extensions - -cache: - directories: - - $HOME/.composer/cache/files - -install: - - ./travis-init.sh - - composer install - -script: - - ./vendor/bin/phpunit --coverage-text diff --git a/vendor/react/event-loop/CHANGELOG.md b/vendor/react/event-loop/CHANGELOG.md index 1bd7f5f..502996b 100644 --- a/vendor/react/event-loop/CHANGELOG.md +++ b/vendor/react/event-loop/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## 1.1.1 (2020-01-01) + +* Fix: Fix reporting connection refused errors with `ExtUvLoop` on Linux and `StreamSelectLoop` on Windows. + (#207 and #208 by @clue) + +* Fix: Fix unsupported EventConfig and `SEGFAULT` on shutdown with `ExtEventLoop` on Windows. + (#205 by @clue) + +* Fix: Check PCNTL functions for signal support instead of PCNTL extension with `StreamSelectLoop`. + (#195 by @clue) + +* Add `.gitattributes` to exclude dev files from exports. + (#201 by @reedy) + +* Improve test suite to fix testing `ExtUvLoop` on Travis, + fix Travis CI builds, do not install `libuv` on legacy PHP setups, + fix failing test cases due to inaccurate timers, + run tests on Windows via Travis CI and + run tests on PHP 7.4 and simplify test matrix and test setup. + (#197 by @WyriHaximus and #202, #203, #204 and #209 by @clue) + +## 1.1.0 (2019-02-07) + +* New UV based event loop (ext-uv). + (#112 by @WyriHaximus) + +* Use high resolution timer on PHP 7.3+. + (#182 by @clue) + +* Improve PCNTL signals by using async signal dispatching if available. + (#179 by @CharlotteDunois) + +* Improve test suite and test suite set up. + (#174 by @WyriHaximus, #181 by @clue) + +* Fix PCNTL signals edge case. + (#183 by @clue) + ## 1.0.0 (2018-07-11) * First stable LTS release, now following [SemVer](https://semver.org/). diff --git a/vendor/react/event-loop/README.md b/vendor/react/event-loop/README.md index 40cb822..cdff1c8 100644 --- a/vendor/react/event-loop/README.md +++ b/vendor/react/event-loop/README.md @@ -21,6 +21,7 @@ single [`run()`](#run) call that is controlled by the user. * [ExtLibeventLoop](#extlibeventloop) * [ExtLibevLoop](#extlibevloop) * [ExtEvLoop](#extevloop) + * [ExtUvLoop](#extuvloop) * [LoopInterface](#loopinterface) * [run()](#run) * [stop()](#stop) @@ -153,7 +154,7 @@ event loop implementation first or they will throw a `BadMethodCallException` on A `stream_select()` based event loop. -This uses the [`stream_select()`](http://php.net/manual/en/function.stream-select.php) +This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php) function and is the only implementation which works out of the box with PHP. This event loop works out of the box on PHP 5.3 through PHP 7+ and HHVM. @@ -181,13 +182,14 @@ It is commonly installed as part of many PHP distributions. If this extension is missing (or you're running on Windows), signal handling is not supported and throws a `BadMethodCallException` instead. -This event loop is known to rely on wall-clock time to schedule future -timers, because a monotonic time source is not available in PHP by default. +This event loop is known to rely on wall-clock time to schedule future timers +when using any version before PHP 7.3, because a monotonic time source is +only available as of PHP 7.3 (`hrtime()`). While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). -This means that if you schedule a timer to trigger in 30s and then adjust -your system time forward by 20s, the timer may trigger in 10s. +This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and +then adjust your system time forward by 20s, the timer may trigger in 10s. See also [`addTimer()`](#addtimer) for more details. #### ExtEventLoop @@ -208,6 +210,14 @@ provides an interface to `libev` library. This loop is known to work with PHP 5.4 through PHP 7+. +#### ExtUvLoop + +An `ext-uv` based event loop. + +This loop uses the [`uv` PECL extension](https://pecl.php.net/package/uv), that +provides an interface to `libuv` library. + +This loop is known to work with PHP 7+. #### ExtLibeventLoop @@ -351,8 +361,8 @@ same time (within its possible accuracy) is not guaranteed. This interface suggests that event loop implementations SHOULD use a monotonic time source if available. Given that a monotonic time source is -not available on PHP by default, event loop implementations MAY fall back -to using wall-clock time. +only available as of PHP 7.3 by default, event loop implementations MAY +fall back to using wall-clock time. While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). @@ -424,8 +434,8 @@ same time (within its possible accuracy) is not guaranteed. This interface suggests that event loop implementations SHOULD use a monotonic time source if available. Given that a monotonic time source is -not available on PHP by default, event loop implementations MAY fall back -to using wall-clock time. +only available as of PHP 7.3 by default, event loop implementations MAY +fall back to using wall-clock time. While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). @@ -662,7 +672,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require react/event-loop:^1.0 +$ composer require react/event-loop:^1.1.1 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. diff --git a/vendor/react/event-loop/composer.json b/vendor/react/event-loop/composer.json index c8ff91e..cc6abf0 100644 --- a/vendor/react/event-loop/composer.json +++ b/vendor/react/event-loop/composer.json @@ -7,11 +7,12 @@ "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "suggest": { "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" }, "autoload": { "psr-4": { diff --git a/vendor/react/event-loop/examples/01-timers.php b/vendor/react/event-loop/examples/01-timers.php deleted file mode 100644 index e6107e4..0000000 --- a/vendor/react/event-loop/examples/01-timers.php +++ /dev/null @@ -1,15 +0,0 @@ -addTimer(0.8, function () { - echo 'world!' . PHP_EOL; -}); - -$loop->addTimer(0.3, function () { - echo 'hello '; -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/02-periodic.php b/vendor/react/event-loop/examples/02-periodic.php deleted file mode 100644 index 5e138a6..0000000 --- a/vendor/react/event-loop/examples/02-periodic.php +++ /dev/null @@ -1,16 +0,0 @@ -addPeriodicTimer(0.1, function () { - echo 'tick!' . PHP_EOL; -}); - -$loop->addTimer(1.0, function () use ($loop, $timer) { - $loop->cancelTimer($timer); - echo 'Done' . PHP_EOL; -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/03-ticks.php b/vendor/react/event-loop/examples/03-ticks.php deleted file mode 100644 index 3f36c6d..0000000 --- a/vendor/react/event-loop/examples/03-ticks.php +++ /dev/null @@ -1,15 +0,0 @@ -futureTick(function () { - echo 'b'; -}); -$loop->futureTick(function () { - echo 'c'; -}); -echo 'a'; - -$loop->run(); diff --git a/vendor/react/event-loop/examples/04-signals.php b/vendor/react/event-loop/examples/04-signals.php deleted file mode 100644 index 90b6898..0000000 --- a/vendor/react/event-loop/examples/04-signals.php +++ /dev/null @@ -1,19 +0,0 @@ -addSignal(SIGINT, $func = function ($signal) use ($loop, &$func) { - echo 'Signal: ', (string)$signal, PHP_EOL; - $loop->removeSignal(SIGINT, $func); -}); - -echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL; - -$loop->run(); diff --git a/vendor/react/event-loop/examples/11-consume-stdin.php b/vendor/react/event-loop/examples/11-consume-stdin.php deleted file mode 100644 index 2a77245..0000000 --- a/vendor/react/event-loop/examples/11-consume-stdin.php +++ /dev/null @@ -1,30 +0,0 @@ -addReadStream(STDIN, function ($stream) use ($loop) { - $chunk = fread($stream, 64 * 1024); - - // reading nothing means we reached EOF - if ($chunk === '') { - $loop->removeReadStream($stream); - stream_set_blocking($stream, true); - fclose($stream); - return; - } - - echo strlen($chunk) . ' bytes' . PHP_EOL; -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/12-generate-yes.php b/vendor/react/event-loop/examples/12-generate-yes.php deleted file mode 100644 index ebc2beb..0000000 --- a/vendor/react/event-loop/examples/12-generate-yes.php +++ /dev/null @@ -1,41 +0,0 @@ -addWriteStream(STDOUT, function ($stdout) use ($loop, &$data) { - // try to write data - $r = fwrite($stdout, $data); - - // nothing could be written despite being writable => closed - if ($r === 0) { - $loop->removeWriteStream($stdout); - fclose($stdout); - stream_set_blocking($stdout, true); - fwrite(STDERR, 'Stopped because STDOUT closed' . PHP_EOL); - - return; - } - - // implement a very simple ring buffer, unless everything has been written at once: - // everything written in this iteration will be appended for next iteration - if (isset($data[$r])) { - $data = substr($data, $r) . substr($data, 0, $r); - } -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/13-http-client-blocking.php b/vendor/react/event-loop/examples/13-http-client-blocking.php deleted file mode 100644 index a2dde55..0000000 --- a/vendor/react/event-loop/examples/13-http-client-blocking.php +++ /dev/null @@ -1,35 +0,0 @@ -addReadStream($stream, function ($stream) use ($loop) { - $chunk = fread($stream, 64 * 1024); - - // reading nothing means we reached EOF - if ($chunk === '') { - echo '[END]' . PHP_EOL; - $loop->removeReadStream($stream); - fclose($stream); - return; - } - - echo $chunk; -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/14-http-client-async.php b/vendor/react/event-loop/examples/14-http-client-async.php deleted file mode 100644 index c82c988..0000000 --- a/vendor/react/event-loop/examples/14-http-client-async.php +++ /dev/null @@ -1,63 +0,0 @@ -addPeriodicTimer(0.01, function () { - echo '.'; -}); - -// wait for connection success/error -$loop->addWriteStream($stream, function ($stream) use ($loop, $timer) { - $loop->removeWriteStream($stream); - $loop->cancelTimer($timer); - - // check for socket error (connection rejected) - if (stream_socket_get_name($stream, true) === false) { - echo '[unable to connect]' . PHP_EOL; - exit(1); - } else { - echo '[connected]' . PHP_EOL; - } - - // send HTTP request - fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"); - - // wait for HTTP response - $loop->addReadStream($stream, function ($stream) use ($loop) { - $chunk = fread($stream, 64 * 1024); - - // reading nothing means we reached EOF - if ($chunk === '') { - echo '[END]' . PHP_EOL; - $loop->removeReadStream($stream); - fclose($stream); - return; - } - - echo $chunk; - }); -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/21-http-server.php b/vendor/react/event-loop/examples/21-http-server.php deleted file mode 100644 index 89520ce..0000000 --- a/vendor/react/event-loop/examples/21-http-server.php +++ /dev/null @@ -1,36 +0,0 @@ -addReadStream($server, function ($server) use ($loop) { - $conn = stream_socket_accept($server); - $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n"; - $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) { - $written = fwrite($conn, $data); - if ($written === strlen($data)) { - fclose($conn); - $loop->removeWriteStream($conn); - } else { - $data = substr($data, $written); - } - }); -}); - -$loop->addPeriodicTimer(5, function () { - $memory = memory_get_usage() / 1024; - $formatted = number_format($memory, 3).'K'; - echo "Current memory usage: {$formatted}\n"; -}); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/91-benchmark-ticks.php b/vendor/react/event-loop/examples/91-benchmark-ticks.php deleted file mode 100644 index 3f4690b..0000000 --- a/vendor/react/event-loop/examples/91-benchmark-ticks.php +++ /dev/null @@ -1,15 +0,0 @@ -futureTick(function () { }); -} - -$loop->run(); diff --git a/vendor/react/event-loop/examples/92-benchmark-timers.php b/vendor/react/event-loop/examples/92-benchmark-timers.php deleted file mode 100644 index e2e02e4..0000000 --- a/vendor/react/event-loop/examples/92-benchmark-timers.php +++ /dev/null @@ -1,15 +0,0 @@ -addTimer(0, function () { }); -} - -$loop->run(); diff --git a/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php b/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php deleted file mode 100644 index 95ee78c..0000000 --- a/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php +++ /dev/null @@ -1,22 +0,0 @@ - 0) { - --$ticks; - //$loop->addTimer(0, $tick); - $loop->futureTick($tick); - } else { - echo 'done'; - } -}; - -$tick(); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/94-benchmark-timers-delay.php b/vendor/react/event-loop/examples/94-benchmark-timers-delay.php deleted file mode 100644 index 2d6cfa2..0000000 --- a/vendor/react/event-loop/examples/94-benchmark-timers-delay.php +++ /dev/null @@ -1,22 +0,0 @@ - 0) { - --$ticks; - //$loop->futureTick($tick); - $loop->addTimer(0, $tick); - } else { - echo 'done'; - } -}; - -$tick(); - -$loop->run(); diff --git a/vendor/react/event-loop/examples/95-benchmark-memory.php b/vendor/react/event-loop/examples/95-benchmark-memory.php deleted file mode 100644 index 084c404..0000000 --- a/vendor/react/event-loop/examples/95-benchmark-memory.php +++ /dev/null @@ -1,67 +0,0 @@ -addTimer($t, function () use ($loop) { - $loop->stop(); - }); - -} - -$loop->addPeriodicTimer(0.001, function () use (&$runs, $loop) { - $runs++; - - $loop->addPeriodicTimer(1, function (TimerInterface $timer) use ($loop) { - $loop->cancelTimer($timer); - }); -}); - -$loop->addPeriodicTimer($r, function () use (&$runs) { - $kmem = round(memory_get_usage() / 1024); - $kmemReal = round(memory_get_usage(true) / 1024); - echo "Runs:\t\t\t$runs\n"; - echo "Memory (internal):\t$kmem KiB\n"; - echo "Memory (real):\t\t$kmemReal KiB\n"; - echo str_repeat('-', 50), "\n"; -}); - -echo "PHP Version:\t\t", phpversion(), "\n"; -echo "Loop\t\t\t", get_class($loop), "\n"; -echo "Time\t\t\t", date('r'), "\n"; - -echo str_repeat('-', 50), "\n"; - -$beginTime = time(); -$loop->run(); -$endTime = time(); -$timeTaken = $endTime - $beginTime; - -echo "PHP Version:\t\t", phpversion(), "\n"; -echo "Loop\t\t\t", get_class($loop), "\n"; -echo "Time\t\t\t", date('r'), "\n"; -echo "Time taken\t\t", $timeTaken, " seconds\n"; -echo "Runs per second\t\t", round($runs / $timeTaken), "\n"; diff --git a/vendor/react/event-loop/phpunit.xml.dist b/vendor/react/event-loop/phpunit.xml.dist deleted file mode 100644 index cba6d4d..0000000 --- a/vendor/react/event-loop/phpunit.xml.dist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - ./tests/ - - - - - - ./src/ - - - diff --git a/vendor/react/event-loop/src/ExtEventLoop.php b/vendor/react/event-loop/src/ExtEventLoop.php index fd403d4..1f1b9ea 100644 --- a/vendor/react/event-loop/src/ExtEventLoop.php +++ b/vendor/react/event-loop/src/ExtEventLoop.php @@ -5,7 +5,6 @@ use BadMethodCallException; use Event; use EventBase; -use EventConfig as EventBaseConfig; use React\EventLoop\Tick\FutureTickQueue; use React\EventLoop\Timer\Timer; use SplObjectStorage; @@ -43,8 +42,13 @@ public function __construct() throw new BadMethodCallException('Cannot create ExtEventLoop, ext-event extension missing'); } - $config = new EventBaseConfig(); - $config->requireFeatures(EventBaseConfig::FEATURE_FDS); + // support arbitrary file descriptors and not just sockets + // Windows only has limited file descriptor support, so do not require this (will fail otherwise) + // @link http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html#_setting_up_a_complicated_event_base + $config = new \EventConfig(); + if (\DIRECTORY_SEPARATOR !== '\\') { + $config->requireFeatures(\EventConfig::FEATURE_FDS); + } $this->eventBase = new EventBase($config); $this->futureTickQueue = new FutureTickQueue(); @@ -55,6 +59,17 @@ public function __construct() $this->createStreamCallback(); } + public function __destruct() + { + // explicitly clear all references to Event objects to prevent SEGFAULTs on Windows + foreach ($this->timerEvents as $timer) { + $this->timerEvents->detach($timer); + } + + $this->readEvents = array(); + $this->writeEvents = array(); + } + public function addReadStream($stream, $listener) { $key = (int) $stream; diff --git a/vendor/react/event-loop/src/ExtUvLoop.php b/vendor/react/event-loop/src/ExtUvLoop.php new file mode 100644 index 0000000..002d6a2 --- /dev/null +++ b/vendor/react/event-loop/src/ExtUvLoop.php @@ -0,0 +1,341 @@ +uv = \uv_loop_new(); + $this->futureTickQueue = new FutureTickQueue(); + $this->timers = new SplObjectStorage(); + $this->streamListener = $this->createStreamListener(); + $this->signals = new SignalsHandler(); + } + + /** + * Returns the underlying ext-uv event loop. (Internal ReactPHP use only.) + * + * @internal + * + * @return resource + */ + public function getUvLoop() + { + return $this->uv; + } + + /** + * {@inheritdoc} + */ + public function addReadStream($stream, $listener) + { + if (isset($this->readStreams[(int) $stream])) { + return; + } + + $this->readStreams[(int) $stream] = $listener; + $this->addStream($stream); + } + + /** + * {@inheritdoc} + */ + public function addWriteStream($stream, $listener) + { + if (isset($this->writeStreams[(int) $stream])) { + return; + } + + $this->writeStreams[(int) $stream] = $listener; + $this->addStream($stream); + } + + /** + * {@inheritdoc} + */ + public function removeReadStream($stream) + { + if (!isset($this->streamEvents[(int) $stream])) { + return; + } + + unset($this->readStreams[(int) $stream]); + $this->removeStream($stream); + } + + /** + * {@inheritdoc} + */ + public function removeWriteStream($stream) + { + if (!isset($this->streamEvents[(int) $stream])) { + return; + } + + unset($this->writeStreams[(int) $stream]); + $this->removeStream($stream); + } + + /** + * {@inheritdoc} + */ + public function addTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, false); + + $that = $this; + $timers = $this->timers; + $callback = function () use ($timer, $timers, $that) { + \call_user_func($timer->getCallback(), $timer); + + if ($timers->contains($timer)) { + $that->cancelTimer($timer); + } + }; + + $event = \uv_timer_init($this->uv); + $this->timers->attach($timer, $event); + \uv_timer_start( + $event, + $this->convertFloatSecondsToMilliseconds($interval), + 0, + $callback + ); + + return $timer; + } + + /** + * {@inheritdoc} + */ + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $callback = function () use ($timer) { + \call_user_func($timer->getCallback(), $timer); + }; + + $interval = $this->convertFloatSecondsToMilliseconds($interval); + $event = \uv_timer_init($this->uv); + $this->timers->attach($timer, $event); + \uv_timer_start( + $event, + $interval, + (int) $interval === 0 ? 1 : $interval, + $callback + ); + + return $timer; + } + + /** + * {@inheritdoc} + */ + public function cancelTimer(TimerInterface $timer) + { + if (isset($this->timers[$timer])) { + @\uv_timer_stop($this->timers[$timer]); + $this->timers->detach($timer); + } + } + + /** + * {@inheritdoc} + */ + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function addSignal($signal, $listener) + { + $this->signals->add($signal, $listener); + + if (!isset($this->signalEvents[$signal])) { + $signals = $this->signals; + $this->signalEvents[$signal] = \uv_signal_init($this->uv); + \uv_signal_start($this->signalEvents[$signal], function () use ($signals, $signal) { + $signals->call($signal); + }, $signal); + } + } + + public function removeSignal($signal, $listener) + { + $this->signals->remove($signal, $listener); + + if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) { + \uv_signal_stop($this->signalEvents[$signal]); + unset($this->signalEvents[$signal]); + } + } + + /** + * {@inheritdoc} + */ + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $hasPendingCallbacks = !$this->futureTickQueue->isEmpty(); + $wasJustStopped = !$this->running; + $nothingLeftToDo = !$this->readStreams + && !$this->writeStreams + && !$this->timers->count() + && $this->signals->isEmpty(); + + // Use UV::RUN_ONCE when there are only I/O events active in the loop and block until one of those triggers, + // otherwise use UV::RUN_NOWAIT. + // @link http://docs.libuv.org/en/v1.x/loop.html#c.uv_run + $flags = \UV::RUN_ONCE; + if ($wasJustStopped || $hasPendingCallbacks) { + $flags = \UV::RUN_NOWAIT; + } elseif ($nothingLeftToDo) { + break; + } + + \uv_run($this->uv, $flags); + } + } + + /** + * {@inheritdoc} + */ + public function stop() + { + $this->running = false; + } + + private function addStream($stream) + { + if (!isset($this->streamEvents[(int) $stream])) { + $this->streamEvents[(int)$stream] = \uv_poll_init_socket($this->uv, $stream); + } + + if ($this->streamEvents[(int) $stream] !== false) { + $this->pollStream($stream); + } + } + + private function removeStream($stream) + { + if (!isset($this->streamEvents[(int) $stream])) { + return; + } + + if (!isset($this->readStreams[(int) $stream]) + && !isset($this->writeStreams[(int) $stream])) { + \uv_poll_stop($this->streamEvents[(int) $stream]); + \uv_close($this->streamEvents[(int) $stream]); + unset($this->streamEvents[(int) $stream]); + return; + } + + $this->pollStream($stream); + } + + private function pollStream($stream) + { + if (!isset($this->streamEvents[(int) $stream])) { + return; + } + + $flags = 0; + if (isset($this->readStreams[(int) $stream])) { + $flags |= \UV::READABLE; + } + + if (isset($this->writeStreams[(int) $stream])) { + $flags |= \UV::WRITABLE; + } + + \uv_poll_start($this->streamEvents[(int) $stream], $flags, $this->streamListener); + } + + /** + * Create a stream listener + * + * @return callable Returns a callback + */ + private function createStreamListener() + { + $callback = function ($event, $status, $events, $stream) { + // libuv automatically stops polling on error, re-enable polling to match other loop implementations + if ($status !== 0) { + $this->pollStream($stream); + + // libuv may report no events on error, but this should still invoke stream listeners to report closed connections + // re-enable both readable and writable, correct listeners will be checked below anyway + if ($events === 0) { + $events = \UV::READABLE | \UV::WRITABLE; + } + } + + if (isset($this->readStreams[(int) $stream]) && ($events & \UV::READABLE)) { + \call_user_func($this->readStreams[(int) $stream], $stream); + } + + if (isset($this->writeStreams[(int) $stream]) && ($events & \UV::WRITABLE)) { + \call_user_func($this->writeStreams[(int) $stream], $stream); + } + }; + + return $callback; + } + + /** + * @param float $interval + * @return int + */ + private function convertFloatSecondsToMilliseconds($interval) + { + if ($interval < 0) { + return 0; + } + + $maxValue = (int) (\PHP_INT_MAX / 1000); + $intInterval = (int) $interval; + + if (($intInterval <= 0 && $interval > 1) || $intInterval >= $maxValue) { + throw new \InvalidArgumentException( + "Interval overflow, value must be lower than '{$maxValue}', but '{$interval}' passed." + ); + } + + return (int) \floor($interval * 1000); + } +} diff --git a/vendor/react/event-loop/src/Factory.php b/vendor/react/event-loop/src/Factory.php index 763c077..d1767bf 100644 --- a/vendor/react/event-loop/src/Factory.php +++ b/vendor/react/event-loop/src/Factory.php @@ -24,14 +24,17 @@ final class Factory public static function create() { // @codeCoverageIgnoreStart - if (\class_exists('libev\EventLoop', false)) { + if (\function_exists('uv_loop_new')) { + // only use ext-uv on PHP 7 + return new ExtUvLoop(); + } elseif (\class_exists('libev\EventLoop', false)) { return new ExtLibevLoop(); } elseif (\class_exists('EvLoop', false)) { return new ExtEvLoop(); } elseif (\class_exists('EventBase', false)) { return new ExtEventLoop(); - } elseif (\function_exists('event_base_new') && \PHP_VERSION_ID < 70000) { - // only use ext-libevent on PHP < 7 for now + } elseif (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) { + // only use ext-libevent on PHP 5 for now return new ExtLibeventLoop(); } diff --git a/vendor/react/event-loop/src/LoopInterface.php b/vendor/react/event-loop/src/LoopInterface.php index 1cc8640..8146757 100644 --- a/vendor/react/event-loop/src/LoopInterface.php +++ b/vendor/react/event-loop/src/LoopInterface.php @@ -185,8 +185,8 @@ public function removeWriteStream($stream); * * This interface suggests that event loop implementations SHOULD use a * monotonic time source if available. Given that a monotonic time source is - * not available on PHP by default, event loop implementations MAY fall back - * to using wall-clock time. + * only available as of PHP 7.3 by default, event loop implementations MAY + * fall back to using wall-clock time. * While this does not affect many common use cases, this is an important * distinction for programs that rely on a high time precision or on systems * that are subject to discontinuous time adjustments (time jumps). @@ -263,8 +263,8 @@ public function addTimer($interval, $callback); * * This interface suggests that event loop implementations SHOULD use a * monotonic time source if available. Given that a monotonic time source is - * not available on PHP by default, event loop implementations MAY fall back - * to using wall-clock time. + * only available as of PHP 7.3 by default, event loop implementations MAY + * fall back to using wall-clock time. * While this does not affect many common use cases, this is an important * distinction for programs that rely on a high time precision or on systems * that are subject to discontinuous time adjustments (time jumps). diff --git a/vendor/react/event-loop/src/StreamSelectLoop.php b/vendor/react/event-loop/src/StreamSelectLoop.php index 625b6fb..b89d800 100644 --- a/vendor/react/event-loop/src/StreamSelectLoop.php +++ b/vendor/react/event-loop/src/StreamSelectLoop.php @@ -2,7 +2,6 @@ namespace React\EventLoop; -use React\EventLoop\Signal\Pcntl; use React\EventLoop\Tick\FutureTickQueue; use React\EventLoop\Timer\Timer; use React\EventLoop\Timer\Timers; @@ -10,7 +9,7 @@ /** * A `stream_select()` based event loop. * - * This uses the [`stream_select()`](http://php.net/manual/en/function.stream-select.php) + * This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php) * function and is the only implementation which works out of the box with PHP. * * This event loop works out of the box on PHP 5.4 through PHP 7+ and HHVM. @@ -38,16 +37,17 @@ * If this extension is missing (or you're running on Windows), signal handling is * not supported and throws a `BadMethodCallException` instead. * - * This event loop is known to rely on wall-clock time to schedule future - * timers, because a monotonic time source is not available in PHP by default. + * This event loop is known to rely on wall-clock time to schedule future timers + * when using any version before PHP 7.3, because a monotonic time source is + * only available as of PHP 7.3 (`hrtime()`). * While this does not affect many common use cases, this is an important * distinction for programs that rely on a high time precision or on systems * that are subject to discontinuous time adjustments (time jumps). - * This means that if you schedule a timer to trigger in 30s and then adjust - * your system time forward by 20s, the timer may trigger in 10s. + * This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and + * then adjust your system time forward by 20s, the timer may trigger in 10s. * See also [`addTimer()`](#addtimer) for more details. * - * @link http://php.net/manual/en/function.stream-select.php + * @link https://www.php.net/manual/en/function.stream-select.php */ final class StreamSelectLoop implements LoopInterface { @@ -62,14 +62,21 @@ final class StreamSelectLoop implements LoopInterface private $writeListeners = array(); private $running; private $pcntl = false; + private $pcntlPoll = false; private $signals; public function __construct() { $this->futureTickQueue = new FutureTickQueue(); $this->timers = new Timers(); - $this->pcntl = \extension_loaded('pcntl'); + $this->pcntl = \function_exists('pcntl_signal') && \function_exists('pcntl_signal_dispatch'); + $this->pcntlPoll = $this->pcntl && !\function_exists('pcntl_async_signals'); $this->signals = new SignalsHandler(); + + // prefer async signals if available (PHP 7.1+) or fall back to dispatching on each tick + if ($this->pcntl && !$this->pcntlPoll) { + \pcntl_async_signals(true); + } } public function addReadStream($stream, $listener) @@ -222,7 +229,7 @@ private function waitForStreamActivity($timeout) $write = $this->writeStreams; $available = $this->streamSelect($read, $write, $timeout); - if ($this->pcntl) { + if ($this->pcntlPoll) { \pcntl_signal_dispatch(); } if (false === $available) { @@ -252,23 +259,49 @@ private function waitForStreamActivity($timeout) * Emulate a stream_select() implementation that does not break when passed * empty stream arrays. * - * @param array &$read An array of read streams to select upon. - * @param array &$write An array of write streams to select upon. - * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever. + * @param array $read An array of read streams to select upon. + * @param array $write An array of write streams to select upon. + * @param int|null $timeout Activity timeout in microseconds, or null to wait forever. * - * @return integer|false The total number of streams that are ready for read/write. - * Can return false if stream_select() is interrupted by a signal. + * @return int|false The total number of streams that are ready for read/write. + * Can return false if stream_select() is interrupted by a signal. */ private function streamSelect(array &$read, array &$write, $timeout) { if ($read || $write) { + // We do not usually use or expose the `exceptfds` parameter passed to the underlying `select`. + // However, Windows does not report failed connection attempts in `writefds` passed to `select` like most other platforms. + // Instead, it uses `writefds` only for successful connection attempts and `exceptfds` for failed connection attempts. + // We work around this by adding all sockets that look like a pending connection attempt to `exceptfds` automatically on Windows and merge it back later. + // This ensures the public API matches other loop implementations across all platforms (see also test suite or rather test matrix). + // Lacking better APIs, every write-only socket that has not yet read any data is assumed to be in a pending connection attempt state. + // @link https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select $except = null; + if (\DIRECTORY_SEPARATOR === '\\') { + $except = array(); + foreach ($write as $key => $socket) { + if (!isset($read[$key]) && @\ftell($socket) === 0) { + $except[$key] = $socket; + } + } + } // suppress warnings that occur, when stream_select is interrupted by a signal - return @\stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout); + $ret = @\stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout); + + if ($except) { + $write = \array_merge($write, $except); + } + return $ret; } - $timeout && \usleep($timeout); + if ($timeout > 0) { + \usleep($timeout); + } elseif ($timeout === null) { + // wait forever (we only reach this if we're only awaiting signals) + // this may be interrupted and return earlier when a signal is received + \sleep(PHP_INT_MAX); + } return 0; } diff --git a/vendor/react/event-loop/src/Timer/Timers.php b/vendor/react/event-loop/src/Timer/Timers.php index 1d4ac9b..70adc13 100644 --- a/vendor/react/event-loop/src/Timer/Timers.php +++ b/vendor/react/event-loop/src/Timer/Timers.php @@ -18,10 +18,17 @@ final class Timers private $timers = array(); private $schedule = array(); private $sorted = true; + private $useHighResolution; + + public function __construct() + { + // prefer high-resolution timer, available as of PHP 7.3+ + $this->useHighResolution = \function_exists('hrtime'); + } public function updateTime() { - return $this->time = \microtime(true); + return $this->time = $this->useHighResolution ? \hrtime(true) * 1e-9 : \microtime(true); } public function getTime() @@ -33,7 +40,7 @@ public function add(TimerInterface $timer) { $id = \spl_object_hash($timer); $this->timers[$id] = $timer; - $this->schedule[$id] = $timer->getInterval() + \microtime(true); + $this->schedule[$id] = $timer->getInterval() + $this->updateTime(); $this->sorted = false; } diff --git a/vendor/react/event-loop/tests/AbstractLoopTest.php b/vendor/react/event-loop/tests/AbstractLoopTest.php deleted file mode 100644 index 3d84438..0000000 --- a/vendor/react/event-loop/tests/AbstractLoopTest.php +++ /dev/null @@ -1,616 +0,0 @@ -tickTimeout = 0.02; - $this->loop = $this->createLoop(); - } - - abstract public function createLoop(); - - public function createSocketPair() - { - $domain = (DIRECTORY_SEPARATOR === '\\') ? STREAM_PF_INET : STREAM_PF_UNIX; - $sockets = stream_socket_pair($domain, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - - foreach ($sockets as $socket) { - if (function_exists('stream_set_read_buffer')) { - stream_set_read_buffer($socket, 0); - } - } - - return $sockets; - } - - public function testAddReadStream() - { - list ($input, $output) = $this->createSocketPair(); - - $this->loop->addReadStream($input, $this->expectCallableExactly(2)); - - fwrite($output, "foo\n"); - $this->tickLoop($this->loop); - - fwrite($output, "bar\n"); - $this->tickLoop($this->loop); - } - - public function testAddReadStreamIgnoresSecondCallable() - { - list ($input, $output) = $this->createSocketPair(); - - $this->loop->addReadStream($input, $this->expectCallableExactly(2)); - $this->loop->addReadStream($input, $this->expectCallableNever()); - - fwrite($output, "foo\n"); - $this->tickLoop($this->loop); - - fwrite($output, "bar\n"); - $this->tickLoop($this->loop); - } - - public function testAddReadStreamReceivesDataFromStreamReference() - { - $this->received = ''; - $this->subAddReadStreamReceivesDataFromStreamReference(); - $this->assertEquals('', $this->received); - - $this->assertRunFasterThan($this->tickTimeout * 2); - $this->assertEquals('[hello]X', $this->received); - } - - /** - * Helper for above test. This happens in another helper method to verify - * the loop keeps track of assigned stream resources (refcount). - */ - private function subAddReadStreamReceivesDataFromStreamReference() - { - list ($input, $output) = $this->createSocketPair(); - - fwrite($input, 'hello'); - fclose($input); - - $loop = $this->loop; - $received =& $this->received; - $loop->addReadStream($output, function ($output) use ($loop, &$received) { - $chunk = fread($output, 1024); - if ($chunk === '') { - $received .= 'X'; - $loop->removeReadStream($output); - fclose($output); - } else { - $received .= '[' . $chunk . ']'; - } - }); - } - - public function testAddWriteStream() - { - list ($input) = $this->createSocketPair(); - - $this->loop->addWriteStream($input, $this->expectCallableExactly(2)); - $this->tickLoop($this->loop); - $this->tickLoop($this->loop); - } - - public function testAddWriteStreamIgnoresSecondCallable() - { - list ($input) = $this->createSocketPair(); - - $this->loop->addWriteStream($input, $this->expectCallableExactly(2)); - $this->loop->addWriteStream($input, $this->expectCallableNever()); - $this->tickLoop($this->loop); - $this->tickLoop($this->loop); - } - - public function testRemoveReadStreamInstantly() - { - list ($input, $output) = $this->createSocketPair(); - - $this->loop->addReadStream($input, $this->expectCallableNever()); - $this->loop->removeReadStream($input); - - fwrite($output, "bar\n"); - $this->tickLoop($this->loop); - } - - public function testRemoveReadStreamAfterReading() - { - list ($input, $output) = $this->createSocketPair(); - - $this->loop->addReadStream($input, $this->expectCallableOnce()); - - fwrite($output, "foo\n"); - $this->tickLoop($this->loop); - - $this->loop->removeReadStream($input); - - fwrite($output, "bar\n"); - $this->tickLoop($this->loop); - } - - public function testRemoveWriteStreamInstantly() - { - list ($input) = $this->createSocketPair(); - - $this->loop->addWriteStream($input, $this->expectCallableNever()); - $this->loop->removeWriteStream($input); - $this->tickLoop($this->loop); - } - - public function testRemoveWriteStreamAfterWriting() - { - list ($input) = $this->createSocketPair(); - - $this->loop->addWriteStream($input, $this->expectCallableOnce()); - $this->tickLoop($this->loop); - - $this->loop->removeWriteStream($input); - $this->tickLoop($this->loop); - } - - public function testRemoveStreamForReadOnly() - { - list ($input, $output) = $this->createSocketPair(); - - $this->loop->addReadStream($input, $this->expectCallableNever()); - $this->loop->addWriteStream($output, $this->expectCallableOnce()); - $this->loop->removeReadStream($input); - - fwrite($output, "foo\n"); - $this->tickLoop($this->loop); - } - - public function testRemoveStreamForWriteOnly() - { - list ($input, $output) = $this->createSocketPair(); - - fwrite($output, "foo\n"); - - $this->loop->addReadStream($input, $this->expectCallableOnce()); - $this->loop->addWriteStream($output, $this->expectCallableNever()); - $this->loop->removeWriteStream($output); - - $this->tickLoop($this->loop); - } - - public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesEndsLoop() - { - list($stream, $other) = $this->createSocketPair(); - stream_set_blocking($stream, false); - stream_set_blocking($other, false); - - // dummy writable handler - $this->loop->addWriteStream($stream, function () { }); - - // remove stream when the stream is readable (closes) - $loop = $this->loop; - $loop->addReadStream($stream, function ($stream) use ($loop) { - $loop->removeReadStream($stream); - $loop->removeWriteStream($stream); - fclose($stream); - }); - - // close other side - fclose($other); - - $this->assertRunFasterThan($this->tickTimeout); - } - - public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesOnEndOfFileEndsLoop() - { - list($stream, $other) = $this->createSocketPair(); - stream_set_blocking($stream, false); - stream_set_blocking($other, false); - - // dummy writable handler - $this->loop->addWriteStream($stream, function () { }); - - // remove stream when the stream is readable (closes) - $loop = $this->loop; - $loop->addReadStream($stream, function ($stream) use ($loop) { - $data = fread($stream, 1024); - if ($data !== '') { - return; - } - - $loop->removeReadStream($stream); - $loop->removeWriteStream($stream); - fclose($stream); - }); - - // send data and close stream - fwrite($other, str_repeat('.', static::PHP_DEFAULT_CHUNK_SIZE)); - $this->loop->addTimer(0.01, function () use ($other) { - fclose($other); - }); - - $this->assertRunFasterThan(0.1); - } - - public function testRemoveReadAndWriteStreamFromLoopWithClosingResourceEndsLoop() - { - // get only one part of the pair to ensure the other side will close immediately - list($stream) = $this->createSocketPair(); - stream_set_blocking($stream, false); - - // dummy writable handler - $this->loop->addWriteStream($stream, function () { }); - - // remove stream when the stream is readable (closes) - $loop = $this->loop; - $loop->addReadStream($stream, function ($stream) use ($loop) { - $loop->removeReadStream($stream); - $loop->removeWriteStream($stream); - fclose($stream); - }); - - $this->assertRunFasterThan($this->tickTimeout); - } - - public function testRemoveInvalid() - { - list ($stream) = $this->createSocketPair(); - - // remove a valid stream from the event loop that was never added in the first place - $this->loop->removeReadStream($stream); - $this->loop->removeWriteStream($stream); - - $this->assertTrue(true); - } - - /** @test */ - public function emptyRunShouldSimplyReturn() - { - $this->assertRunFasterThan($this->tickTimeout); - } - - /** @test */ - public function runShouldReturnWhenNoMoreFds() - { - list ($input, $output) = $this->createSocketPair(); - - $loop = $this->loop; - $this->loop->addReadStream($input, function ($stream) use ($loop) { - $loop->removeReadStream($stream); - }); - - fwrite($output, "foo\n"); - - $this->assertRunFasterThan($this->tickTimeout * 2); - } - - /** @test */ - public function stopShouldStopRunningLoop() - { - list ($input, $output) = $this->createSocketPair(); - - $loop = $this->loop; - $this->loop->addReadStream($input, function ($stream) use ($loop) { - $loop->stop(); - }); - - fwrite($output, "foo\n"); - - $this->assertRunFasterThan($this->tickTimeout * 2); - } - - public function testStopShouldPreventRunFromBlocking() - { - $that = $this; - $this->loop->addTimer( - 1, - function () use ($that) { - $that->fail('Timer was executed.'); - } - ); - - $loop = $this->loop; - $this->loop->futureTick( - function () use ($loop) { - $loop->stop(); - } - ); - - $this->assertRunFasterThan($this->tickTimeout * 2); - } - - public function testIgnoreRemovedCallback() - { - // two independent streams, both should be readable right away - list ($input1, $output1) = $this->createSocketPair(); - list ($input2, $output2) = $this->createSocketPair(); - - $called = false; - - $loop = $this->loop; - $loop->addReadStream($input1, function ($stream) use (& $called, $loop, $input2) { - // stream1 is readable, remove stream2 as well => this will invalidate its callback - $loop->removeReadStream($stream); - $loop->removeReadStream($input2); - - $called = true; - }); - - // this callback would have to be called as well, but the first stream already removed us - $that = $this; - $loop->addReadStream($input2, function () use (& $called, $that) { - if ($called) { - $that->fail('Callback 2 must not be called after callback 1 was called'); - } - }); - - fwrite($output1, "foo\n"); - fwrite($output2, "foo\n"); - - $loop->run(); - - $this->assertTrue($called); - } - - public function testFutureTickEventGeneratedByFutureTick() - { - $loop = $this->loop; - $this->loop->futureTick( - function () use ($loop) { - $loop->futureTick( - function () { - echo 'future-tick' . PHP_EOL; - } - ); - } - ); - - $this->expectOutputString('future-tick' . PHP_EOL); - - $this->loop->run(); - } - - public function testFutureTick() - { - $called = false; - - $callback = function () use (&$called) { - $called = true; - }; - - $this->loop->futureTick($callback); - - $this->assertFalse($called); - - $this->tickLoop($this->loop); - - $this->assertTrue($called); - } - - public function testFutureTickFiresBeforeIO() - { - list ($stream) = $this->createSocketPair(); - - $this->loop->addWriteStream( - $stream, - function () { - echo 'stream' . PHP_EOL; - } - ); - - $this->loop->futureTick( - function () { - echo 'future-tick' . PHP_EOL; - } - ); - - $this->expectOutputString('future-tick' . PHP_EOL . 'stream' . PHP_EOL); - - $this->tickLoop($this->loop); - } - - public function testRecursiveFutureTick() - { - list ($stream) = $this->createSocketPair(); - - $loop = $this->loop; - $this->loop->addWriteStream( - $stream, - function () use ($stream, $loop) { - echo 'stream' . PHP_EOL; - $loop->removeWriteStream($stream); - } - ); - - $this->loop->futureTick( - function () use ($loop) { - echo 'future-tick-1' . PHP_EOL; - $loop->futureTick( - function () { - echo 'future-tick-2' . PHP_EOL; - } - ); - } - ); - - $this->expectOutputString('future-tick-1' . PHP_EOL . 'stream' . PHP_EOL . 'future-tick-2' . PHP_EOL); - - $this->loop->run(); - } - - public function testRunWaitsForFutureTickEvents() - { - list ($stream) = $this->createSocketPair(); - - $loop = $this->loop; - $this->loop->addWriteStream( - $stream, - function () use ($stream, $loop) { - $loop->removeWriteStream($stream); - $loop->futureTick( - function () { - echo 'future-tick' . PHP_EOL; - } - ); - } - ); - - $this->expectOutputString('future-tick' . PHP_EOL); - - $this->loop->run(); - } - - public function testFutureTickEventGeneratedByTimer() - { - $loop = $this->loop; - $this->loop->addTimer( - 0.001, - function () use ($loop) { - $loop->futureTick( - function () { - echo 'future-tick' . PHP_EOL; - } - ); - } - ); - - $this->expectOutputString('future-tick' . PHP_EOL); - - $this->loop->run(); - } - - public function testRemoveSignalNotRegisteredIsNoOp() - { - $this->loop->removeSignal(SIGINT, function () { }); - $this->assertTrue(true); - } - - public function testSignal() - { - if (!function_exists('posix_kill') || !function_exists('posix_getpid')) { - $this->markTestSkipped('Signal test skipped because functions "posix_kill" and "posix_getpid" are missing.'); - } - - $called = false; - $calledShouldNot = true; - - $timer = $this->loop->addPeriodicTimer(1, function () {}); - - $this->loop->addSignal(SIGUSR2, $func2 = function () use (&$calledShouldNot) { - $calledShouldNot = false; - }); - - $loop = $this->loop; - $this->loop->addSignal(SIGUSR1, $func1 = function () use (&$func1, &$func2, &$called, $timer, $loop) { - $called = true; - $loop->removeSignal(SIGUSR1, $func1); - $loop->removeSignal(SIGUSR2, $func2); - $loop->cancelTimer($timer); - }); - - $this->loop->futureTick(function () { - posix_kill(posix_getpid(), SIGUSR1); - }); - - $this->loop->run(); - - $this->assertTrue($called); - $this->assertTrue($calledShouldNot); - } - - public function testSignalMultipleUsagesForTheSameListener() - { - $funcCallCount = 0; - $func = function () use (&$funcCallCount) { - $funcCallCount++; - }; - $this->loop->addTimer(1, function () {}); - - $this->loop->addSignal(SIGUSR1, $func); - $this->loop->addSignal(SIGUSR1, $func); - - $this->loop->addTimer(0.4, function () { - posix_kill(posix_getpid(), SIGUSR1); - }); - $loop = $this->loop; - $this->loop->addTimer(0.9, function () use (&$func, $loop) { - $loop->removeSignal(SIGUSR1, $func); - }); - - $this->loop->run(); - - $this->assertSame(1, $funcCallCount); - } - - public function testSignalsKeepTheLoopRunning() - { - $loop = $this->loop; - $function = function () {}; - $this->loop->addSignal(SIGUSR1, $function); - $this->loop->addTimer(1.5, function () use ($function, $loop) { - $loop->removeSignal(SIGUSR1, $function); - $loop->stop(); - }); - - $this->assertRunSlowerThan(1.5); - } - - public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop() - { - $loop = $this->loop; - $function = function () {}; - $this->loop->addSignal(SIGUSR1, $function); - $this->loop->addTimer(1.5, function () use ($function, $loop) { - $loop->removeSignal(SIGUSR1, $function); - }); - - $this->assertRunFasterThan(1.6); - } - - public function testTimerIntervalCanBeFarInFuture() - { - $loop = $this->loop; - // start a timer very far in the future - $timer = $this->loop->addTimer(PHP_INT_MAX, function () { }); - - $this->loop->futureTick(function () use ($timer, $loop) { - $loop->cancelTimer($timer); - }); - - $this->assertRunFasterThan($this->tickTimeout); - } - - private function assertRunSlowerThan($minInterval) - { - $start = microtime(true); - - $this->loop->run(); - - $end = microtime(true); - $interval = $end - $start; - - $this->assertLessThan($interval, $minInterval); - } - - private function assertRunFasterThan($maxInterval) - { - $start = microtime(true); - - $this->loop->run(); - - $end = microtime(true); - $interval = $end - $start; - - $this->assertLessThan($maxInterval, $interval); - } -} diff --git a/vendor/react/event-loop/tests/CallableStub.php b/vendor/react/event-loop/tests/CallableStub.php deleted file mode 100644 index 913d403..0000000 --- a/vendor/react/event-loop/tests/CallableStub.php +++ /dev/null @@ -1,10 +0,0 @@ -markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.'); - } - - return new ExtEvLoop(); - } -} diff --git a/vendor/react/event-loop/tests/ExtEventLoopTest.php b/vendor/react/event-loop/tests/ExtEventLoopTest.php deleted file mode 100644 index 2f88d18..0000000 --- a/vendor/react/event-loop/tests/ExtEventLoopTest.php +++ /dev/null @@ -1,84 +0,0 @@ -markTestSkipped('libevent tests skipped on linux due to linux epoll issues.'); - } - - if (!extension_loaded('event')) { - $this->markTestSkipped('ext-event tests skipped because ext-event is not installed.'); - } - - return new ExtEventLoop(); - } - - public function createStream() - { - // Use a FIFO on linux to get around lack of support for disk-based file - // descriptors when using the EPOLL back-end. - if ('Linux' === PHP_OS) { - $this->fifoPath = tempnam(sys_get_temp_dir(), 'react-'); - - unlink($this->fifoPath); - - posix_mkfifo($this->fifoPath, 0600); - - $stream = fopen($this->fifoPath, 'r+'); - - // ext-event (as of 1.8.1) does not yet support in-memory temporary - // streams. Setting maxmemory:0 and performing a write forces PHP to - // back this temporary stream with a real file. - // - // This problem is mentioned at https://bugs.php.net/bug.php?id=64652&edit=3 - // but remains unresolved (despite that issue being closed). - } else { - $stream = fopen('php://temp/maxmemory:0', 'r+'); - - fwrite($stream, 'x'); - ftruncate($stream, 0); - } - - return $stream; - } - - public function writeToStream($stream, $content) - { - if ('Linux' !== PHP_OS) { - return parent::writeToStream($stream, $content); - } - - fwrite($stream, $content); - } - - /** - * @group epoll-readable-error - */ - public function testCanUseReadableStreamWithFeatureFds() - { - if (PHP_VERSION_ID > 70000) { - $this->markTestSkipped('Memory stream not supported'); - } - - $this->loop = $this->createLoop(true); - - $input = fopen('php://temp/maxmemory:0', 'r+'); - - fwrite($input, 'x'); - ftruncate($input, 0); - - $this->loop->addReadStream($input, $this->expectCallableExactly(2)); - - fwrite($input, "foo\n"); - $this->tickLoop($this->loop); - - fwrite($input, "bar\n"); - $this->tickLoop($this->loop); - } -} diff --git a/vendor/react/event-loop/tests/ExtLibevLoopTest.php b/vendor/react/event-loop/tests/ExtLibevLoopTest.php deleted file mode 100644 index 19a5e87..0000000 --- a/vendor/react/event-loop/tests/ExtLibevLoopTest.php +++ /dev/null @@ -1,22 +0,0 @@ -markTestSkipped('libev tests skipped because ext-libev is not installed.'); - } - - return new ExtLibevLoop(); - } - - public function testLibEvConstructor() - { - $loop = new ExtLibevLoop(); - } -} diff --git a/vendor/react/event-loop/tests/ExtLibeventLoopTest.php b/vendor/react/event-loop/tests/ExtLibeventLoopTest.php deleted file mode 100644 index 8497065..0000000 --- a/vendor/react/event-loop/tests/ExtLibeventLoopTest.php +++ /dev/null @@ -1,58 +0,0 @@ -markTestSkipped('libevent tests skipped on linux due to linux epoll issues.'); - } - - if (!function_exists('event_base_new')) { - $this->markTestSkipped('libevent tests skipped because ext-libevent is not installed.'); - } - - return new ExtLibeventLoop(); - } - - public function tearDown() - { - if (file_exists($this->fifoPath)) { - unlink($this->fifoPath); - } - } - - public function createStream() - { - if ('Linux' !== PHP_OS) { - return parent::createStream(); - } - - $this->fifoPath = tempnam(sys_get_temp_dir(), 'react-'); - - unlink($this->fifoPath); - - // Use a FIFO on linux to get around lack of support for disk-based file - // descriptors when using the EPOLL back-end. - posix_mkfifo($this->fifoPath, 0600); - - $stream = fopen($this->fifoPath, 'r+'); - - return $stream; - } - - public function writeToStream($stream, $content) - { - if ('Linux' !== PHP_OS) { - return parent::writeToStream($stream, $content); - } - - fwrite($stream, $content); - } -} diff --git a/vendor/react/event-loop/tests/SignalsHandlerTest.php b/vendor/react/event-loop/tests/SignalsHandlerTest.php deleted file mode 100644 index f8b7df3..0000000 --- a/vendor/react/event-loop/tests/SignalsHandlerTest.php +++ /dev/null @@ -1,55 +0,0 @@ -assertSame(0, $callCount); - - $signals->add(SIGUSR1, $func); - $this->assertSame(0, $callCount); - - $signals->add(SIGUSR1, $func); - $this->assertSame(0, $callCount); - - $signals->add(SIGUSR1, $func); - $this->assertSame(0, $callCount); - - $signals->call(SIGUSR1); - $this->assertSame(1, $callCount); - - $signals->add(SIGUSR2, $func); - $this->assertSame(1, $callCount); - - $signals->add(SIGUSR2, $func); - $this->assertSame(1, $callCount); - - $signals->call(SIGUSR2); - $this->assertSame(2, $callCount); - - $signals->remove(SIGUSR2, $func); - $this->assertSame(2, $callCount); - - $signals->remove(SIGUSR2, $func); - $this->assertSame(2, $callCount); - - $signals->call(SIGUSR2); - $this->assertSame(2, $callCount); - - $signals->remove(SIGUSR1, $func); - $this->assertSame(2, $callCount); - - $signals->call(SIGUSR1); - $this->assertSame(2, $callCount); - } -} diff --git a/vendor/react/event-loop/tests/StreamSelectLoopTest.php b/vendor/react/event-loop/tests/StreamSelectLoopTest.php deleted file mode 100644 index bd19e1c..0000000 --- a/vendor/react/event-loop/tests/StreamSelectLoopTest.php +++ /dev/null @@ -1,148 +0,0 @@ -getName(false), 'testSignal', 10) === 0 && extension_loaded('pcntl')) { - $this->resetSignalHandlers(); - } - } - - public function createLoop() - { - return new StreamSelectLoop(); - } - - public function testStreamSelectTimeoutEmulation() - { - $this->loop->addTimer( - 0.05, - $this->expectCallableOnce() - ); - - $start = microtime(true); - - $this->loop->run(); - - $end = microtime(true); - $interval = $end - $start; - - $this->assertGreaterThan(0.04, $interval); - } - - public function signalProvider() - { - return array( - array('SIGUSR1'), - array('SIGHUP'), - array('SIGTERM'), - ); - } - - /** - * Test signal interrupt when no stream is attached to the loop - * @dataProvider signalProvider - */ - public function testSignalInterruptNoStream($signal) - { - if (!extension_loaded('pcntl')) { - $this->markTestSkipped('"pcntl" extension is required to run this test.'); - } - - // dispatch signal handler every 10ms for 0.1s - $check = $this->loop->addPeriodicTimer(0.01, function() { - pcntl_signal_dispatch(); - }); - $loop = $this->loop; - $loop->addTimer(0.1, function () use ($check, $loop) { - $loop->cancelTimer($check); - }); - - $handled = false; - $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) { - $handled = true; - })); - - // spawn external process to send signal to current process id - $this->forkSendSignal($signal); - - $this->loop->run(); - $this->assertTrue($handled); - } - - /** - * Test signal interrupt when a stream is attached to the loop - * @dataProvider signalProvider - */ - public function testSignalInterruptWithStream($signal) - { - if (!extension_loaded('pcntl')) { - $this->markTestSkipped('"pcntl" extension is required to run this test.'); - } - - // dispatch signal handler every 10ms - $this->loop->addPeriodicTimer(0.01, function() { - pcntl_signal_dispatch(); - }); - - // add stream to the loop - $loop = $this->loop; - list($writeStream, $readStream) = $this->createSocketPair(); - $loop->addReadStream($readStream, function ($stream) use ($loop) { - /** @var $loop LoopInterface */ - $read = fgets($stream); - if ($read === "end loop\n") { - $loop->stop(); - } - }); - $this->loop->addTimer(0.1, function() use ($writeStream) { - fwrite($writeStream, "end loop\n"); - }); - - $handled = false; - $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) { - $handled = true; - })); - - // spawn external process to send signal to current process id - $this->forkSendSignal($signal); - - $this->loop->run(); - - $this->assertTrue($handled); - } - - /** - * reset all signal handlers to default - */ - protected function resetSignalHandlers() - { - foreach($this->signalProvider() as $signal) { - pcntl_signal(constant($signal[0]), SIG_DFL); - } - } - - /** - * fork child process to send signal to current process id - */ - protected function forkSendSignal($signal) - { - $currentPid = posix_getpid(); - $childPid = pcntl_fork(); - if ($childPid == -1) { - $this->fail("Failed to fork child process!"); - } else if ($childPid === 0) { - // this is executed in the child process - usleep(20000); - posix_kill($currentPid, constant($signal)); - die(); - } - } -} diff --git a/vendor/react/event-loop/tests/TestCase.php b/vendor/react/event-loop/tests/TestCase.php deleted file mode 100644 index dbdd54c..0000000 --- a/vendor/react/event-loop/tests/TestCase.php +++ /dev/null @@ -1,53 +0,0 @@ -createCallableMock(); - $mock - ->expects($this->exactly($amount)) - ->method('__invoke'); - - return $mock; - } - - protected function expectCallableOnce() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke'); - - return $mock; - } - - protected function expectCallableNever() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->never()) - ->method('__invoke'); - - return $mock; - } - - protected function createCallableMock() - { - return $this->getMockBuilder('React\Tests\EventLoop\CallableStub')->getMock(); - } - - protected function tickLoop(LoopInterface $loop) - { - $loop->futureTick(function () use ($loop) { - $loop->stop(); - }); - - $loop->run(); - } -} diff --git a/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php b/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php deleted file mode 100644 index 294e683..0000000 --- a/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php +++ /dev/null @@ -1,122 +0,0 @@ -createLoop(); - - $timer = $loop->addTimer(0.001, $this->expectCallableNever()); - - $this->assertInstanceOf('React\EventLoop\TimerInterface', $timer); - $this->assertFalse($timer->isPeriodic()); - } - - public function testAddTimerWillBeInvokedOnceAndBlocksLoopWhenRunning() - { - $loop = $this->createLoop(); - - $loop->addTimer(0.001, $this->expectCallableOnce()); - - $start = microtime(true); - $loop->run(); - $end = microtime(true); - - // make no strict assumptions about actual time interval. - // must be at least 0.001s (1ms) and should not take longer than 0.1s - $this->assertGreaterThanOrEqual(0.001, $end - $start); - $this->assertLessThan(0.1, $end - $start); - } - - public function testAddPeriodicTimerReturnsPeriodicTimerInstance() - { - $loop = $this->createLoop(); - - $periodic = $loop->addPeriodicTimer(0.1, $this->expectCallableNever()); - - $this->assertInstanceOf('React\EventLoop\TimerInterface', $periodic); - $this->assertTrue($periodic->isPeriodic()); - } - - public function testAddPeriodicTimerWillBeInvokedUntilItIsCancelled() - { - $loop = $this->createLoop(); - - $periodic = $loop->addPeriodicTimer(0.1, $this->expectCallableExactly(3)); - - // make no strict assumptions about actual time interval. - // leave some room to ensure this ticks exactly 3 times. - $loop->addTimer(0.399, function () use ($loop, $periodic) { - $loop->cancelTimer($periodic); - }); - - $loop->run(); - } - - public function testAddPeriodicTimerWillBeInvokedWithMaximumAccuracyUntilItIsCancelled() - { - $loop = $this->createLoop(); - - $i = 0; - $periodic = $loop->addPeriodicTimer(0.001, function () use (&$i) { - ++$i; - }); - - $loop->addTimer(0.02, function () use ($loop, $periodic) { - $loop->cancelTimer($periodic); - }); - - $loop->run(); - - // make no strict assumptions about number of invocations. - // we know it must be no more than 20 times and should at least be - // invoked twice for really slow loops - $this->assertLessThanOrEqual(20, $i); - $this->assertGreaterThan(2, $i); - } - - public function testAddPeriodicTimerCancelsItself() - { - $loop = $this->createLoop(); - - $i = 0; - $loop->addPeriodicTimer(0.001, function ($timer) use (&$i, $loop) { - $i++; - - if ($i === 5) { - $loop->cancelTimer($timer); - } - }); - - $start = microtime(true); - $loop->run(); - $end = microtime(true); - - $this->assertEquals(5, $i); - - // make no strict assumptions about time interval. - // 5 invocations must take at least 0.005s (5ms) and should not take - // longer than 0.1s for slower loops. - $this->assertGreaterThanOrEqual(0.005, $end - $start); - $this->assertLessThan(0.1, $end - $start); - } - - public function testMinimumIntervalOneMicrosecond() - { - $loop = $this->createLoop(); - - $timer = $loop->addTimer(0, function () {}); - - $this->assertEquals(0.000001, $timer->getInterval()); - } -} diff --git a/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php b/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php deleted file mode 100644 index bfa9186..0000000 --- a/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php +++ /dev/null @@ -1,17 +0,0 @@ -markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.'); - } - - return new ExtEvLoop(); - } -} diff --git a/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php b/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php deleted file mode 100644 index a7a6d00..0000000 --- a/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php +++ /dev/null @@ -1,17 +0,0 @@ -markTestSkipped('ext-event tests skipped because ext-event is not installed.'); - } - - return new ExtEventLoop(); - } -} diff --git a/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php b/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php deleted file mode 100644 index 65e82be..0000000 --- a/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php +++ /dev/null @@ -1,17 +0,0 @@ -markTestSkipped('libev tests skipped because ext-libev is not installed.'); - } - - return new ExtLibevLoop(); - } -} diff --git a/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php b/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php deleted file mode 100644 index 9089b9a..0000000 --- a/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php +++ /dev/null @@ -1,17 +0,0 @@ -markTestSkipped('libevent tests skipped because ext-libevent is not installed.'); - } - - return new ExtLibeventLoop(); - } -} diff --git a/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php b/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php deleted file mode 100644 index cfe1d7d..0000000 --- a/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php +++ /dev/null @@ -1,13 +0,0 @@ -tick(); - - // simulate a bunch of processing on stream events, - // part of which schedules a future timer... - sleep(1); - $timers->add(new Timer(0.5, function () { - $this->fail("Timer shouldn't be called"); - })); - - $timers->tick(); - - $this->assertTrue(true); - } - - public function testContains() - { - $timers = new Timers(); - - $timer1 = new Timer(0.1, function () {}); - $timer2 = new Timer(0.1, function () {}); - - $timers->add($timer1); - - self::assertTrue($timers->contains($timer1)); - self::assertFalse($timers->contains($timer2)); - } -} diff --git a/vendor/react/event-loop/tests/bootstrap.php b/vendor/react/event-loop/tests/bootstrap.php deleted file mode 100644 index aeb4435..0000000 --- a/vendor/react/event-loop/tests/bootstrap.php +++ /dev/null @@ -1,9 +0,0 @@ -> "$(php -r 'echo php_ini_loaded_file();')" - fi - - # install 'libev' PHP extension (does not support php 7) - if [[ "$TRAVIS_PHP_VERSION" != "7.0" && - "$TRAVIS_PHP_VERSION" != "7.1" && - "$TRAVIS_PHP_VERSION" != "7.2" ]]; then - git clone --recursive https://github.com/m4rw3r/php-libev - pushd php-libev - phpize - ./configure --with-libev - make - make install - popd - echo "extension=libev.so" >> "$(php -r 'echo php_ini_loaded_file();')" - fi - -fi diff --git a/vendor/react/promise-timer/CHANGELOG.md b/vendor/react/promise-timer/CHANGELOG.md index 82dd30e..e5f4ccd 100644 --- a/vendor/react/promise-timer/CHANGELOG.md +++ b/vendor/react/promise-timer/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.5.1 (2019-03-27) + +* Fix: Typo in readme + (#35 by @aak74) + +* Improvement: Only include functions file when functions aren't defined + (#36 by @Niko9911) + ## 1.5.0 (2018-06-13) * Feature: Improve memory consumption by cleaning up garbage references to pending promise without canceller. diff --git a/vendor/react/promise-timer/README.md b/vendor/react/promise-timer/README.md index 118d2c1..419a127 100644 --- a/vendor/react/promise-timer/README.md +++ b/vendor/react/promise-timer/README.md @@ -165,7 +165,7 @@ For more details on the promise cancellation, please refer to the #### Input cancellation -Irrespective of the timout handling, you can also explicitly `cancel()` the +Irrespective of the timeout handling, you can also explicitly `cancel()` the input `$promise` at any time. This means that the `timeout()` handling does not affect cancellation of the input `$promise`, as demonstrated in the following example: diff --git a/vendor/react/promise-timer/composer.json b/vendor/react/promise-timer/composer.json index a5ec626..1fdaddd 100644 --- a/vendor/react/promise-timer/composer.json +++ b/vendor/react/promise-timer/composer.json @@ -12,7 +12,7 @@ ], "autoload": { "psr-4": { "React\\Promise\\Timer\\": "src/" }, - "files": [ "src/functions.php" ] + "files": [ "src/functions_include.php" ] }, "autoload-dev": { "psr-4": { "React\\Tests\\Promise\\Timer\\": "tests/" } diff --git a/vendor/react/promise-timer/src/functions_include.php b/vendor/react/promise-timer/src/functions_include.php new file mode 100644 index 0000000..1d5673a --- /dev/null +++ b/vendor/react/promise-timer/src/functions_include.php @@ -0,0 +1,7 @@ +connect('tls://example.com:443'); + $promise->then(function (ConnectionInterface $conn) use ($loop) { + // … + }, function (Exception $e) { + echo $e->getMessage(); + }); + ``` + +## 1.0.0 (2018-07-11) + +* First stable LTS release, now following [SemVer](https://semver.org/). + We'd like to emphasize that this component is production ready and battle-tested. + We plan to support all long-term support (LTS) releases for at least 24 months, + so you have a rock-solid foundation to build on top of. + +> Contains no other changes, so it's actually fully compatible with the v0.8.12 release. + ## 0.8.12 (2018-06-11) * Feature: Improve memory consumption for failed and cancelled connection attempts. diff --git a/vendor/react/socket/README.md b/vendor/react/socket/README.md index cf9b56b..dd9d0e7 100644 --- a/vendor/react/socket/README.md +++ b/vendor/react/socket/README.md @@ -43,6 +43,7 @@ handle multiple concurrent connections without blocking. * [Connector](#connector) * [Advanced client usage](#advanced-client-usage) * [TcpConnector](#tcpconnector) + * [HappyEyeBallsConnector](#happyeyeballsconnector) * [DnsConnector](#dnsconnector) * [SecureConnector](#secureconnector) * [TimeoutConnector](#timeoutconnector) @@ -60,13 +61,13 @@ Here is a server that closes the connection if you send it anything: $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server('127.0.0.1:8080', $loop); -$socket->on('connection', function (ConnectionInterface $conn) { - $conn->write("Hello " . $conn->getRemoteAddress() . "!\n"); - $conn->write("Welcome to this amazing server!\n"); - $conn->write("Here's a tip: don't say anything.\n"); +$socket->on('connection', function (React\Socket\ConnectionInterface $connection) { + $connection->write("Hello " . $connection->getRemoteAddress() . "!\n"); + $connection->write("Welcome to this amazing server!\n"); + $connection->write("Here's a tip: don't say anything.\n"); - $conn->on('data', function ($data) use ($conn) { - $conn->close(); + $connection->on('data', function ($data) use ($connection) { + $connection->close(); }); }); @@ -82,9 +83,9 @@ send it a string: $loop = React\EventLoop\Factory::create(); $connector = new React\Socket\Connector($loop); -$connector->connect('127.0.0.1:8080')->then(function (ConnectionInterface $conn) use ($loop) { - $conn->pipe(new React\Stream\WritableResourceStream(STDOUT, $loop)); - $conn->write("Hello World!\n"); +$connector->connect('127.0.0.1:8080')->then(function (React\Socket\ConnectionInterface $connection) use ($loop) { + $connection->pipe(new React\Stream\WritableResourceStream(STDOUT, $loop)); + $connection->write("Hello World!\n"); }); $loop->run(); @@ -219,7 +220,7 @@ The `connection` event will be emitted whenever a new connection has been established, i.e. a new client connects to this server socket: ```php -$server->on('connection', function (ConnectionInterface $connection) { +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { echo 'new connection' . PHP_EOL; }); ``` @@ -347,7 +348,7 @@ streaming connections, such as plaintext TCP/IP or secure TLS connection streams Connections can also be accepted on Unix domain sockets. ```php -$server = new Server(8080, $loop); +$server = new React\Socket\Server(8080, $loop); ``` As above, the `$uri` parameter can consist of only a port, in which case the @@ -357,7 +358,7 @@ which means it will not be reachable from outside of this system. In order to use a random port assignment, you can use the port `0`: ```php -$server = new Server(0, $loop); +$server = new React\Socket\Server(0, $loop); $address = $server->getAddress(); ``` @@ -366,21 +367,21 @@ address through the first parameter provided to the constructor, optionally preceded by the `tcp://` scheme: ```php -$server = new Server('192.168.0.1:8080', $loop); +$server = new React\Socket\Server('192.168.0.1:8080', $loop); ``` If you want to listen on an IPv6 address, you MUST enclose the host in square brackets: ```php -$server = new Server('[::1]:8080', $loop); +$server = new React\Socket\Server('[::1]:8080', $loop); ``` To listen on a Unix domain socket (UDS) path, you MUST prefix the URI with the `unix://` scheme: ```php -$server = new Server('unix:///tmp/server.sock', $loop); +$server = new React\Socket\Server('unix:///tmp/server.sock', $loop); ``` If the given URI is invalid, does not contain a port, any other scheme or if it @@ -388,7 +389,7 @@ contains a hostname, it will throw an `InvalidArgumentException`: ```php // throws InvalidArgumentException due to missing port -$server = new Server('127.0.0.1', $loop); +$server = new React\Socket\Server('127.0.0.1', $loop); ``` If the given URI appears to be valid, but listening on it fails (such as if port @@ -396,10 +397,10 @@ is already in use or port below 1024 may require root access etc.), it will throw a `RuntimeException`: ```php -$first = new Server(8080, $loop); +$first = new React\Socket\Server(8080, $loop); // throws RuntimeException because port is already in use -$second = new Server(8080, $loop); +$second = new React\Socket\Server(8080, $loop); ``` > Note that these error conditions may vary depending on your system and/or @@ -407,11 +408,11 @@ $second = new Server(8080, $loop); See the exception message and code for more details about the actual error condition. -Optionally, you can specify [TCP socket context options](http://php.net/manual/en/context.socket.php) +Optionally, you can specify [TCP socket context options](https://www.php.net/manual/en/context.socket.php) for the underlying stream socket resource like this: ```php -$server = new Server('[::1]:8080', $loop, array( +$server = new React\Socket\Server('[::1]:8080', $loop, array( 'tcp' => array( 'backlog' => 200, 'so_reuseport' => true, @@ -420,7 +421,7 @@ $server = new Server('[::1]:8080', $loop, array( )); ``` -> Note that available [socket context options](http://php.net/manual/en/context.socket.php), +> Note that available [socket context options](https://www.php.net/manual/en/context.socket.php), their defaults and effects of changing these may vary depending on your system and/or PHP version. Passing unknown context options has no effect. @@ -431,12 +432,12 @@ You can start a secure TLS (formerly known as SSL) server by simply prepending the `tls://` URI scheme. Internally, it will wait for plaintext TCP/IP connections and then performs a TLS handshake for each connection. -It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php), +It thus requires valid [TLS context options](https://www.php.net/manual/en/context.ssl.php), which in its most basic form may look something like this if you're using a PEM encoded certificate file: ```php -$server = new Server('tls://127.0.0.1:8080', $loop, array( +$server = new React\Socket\Server('tls://127.0.0.1:8080', $loop, array( 'tls' => array( 'local_cert' => 'server.pem' ) @@ -452,7 +453,7 @@ If your private key is encrypted with a passphrase, you have to specify it like this: ```php -$server = new Server('tls://127.0.0.1:8000', $loop, array( +$server = new React\Socket\Server('tls://127.0.0.1:8000', $loop, array( 'tls' => array( 'local_cert' => 'server.pem', 'passphrase' => 'secret' @@ -465,7 +466,7 @@ SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you want to negotiate with the remote side: ```php -$server = new Server('tls://127.0.0.1:8000', $loop, array( +$server = new React\Socket\Server('tls://127.0.0.1:8000', $loop, array( 'tls' => array( 'local_cert' => 'server.pem', 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER @@ -473,7 +474,7 @@ $server = new Server('tls://127.0.0.1:8000', $loop, array( )); ``` -> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php), +> Note that available [TLS context options](https://www.php.net/manual/en/context.ssl.php), their defaults and effects of changing these may vary depending on your system and/or PHP version. The outer context array allows you to also use `tcp` (and possibly more) @@ -486,7 +487,7 @@ Whenever a client connects, it will emit a `connection` event with a connection instance implementing [`ConnectionInterface`](#connectioninterface): ```php -$server->on('connection', function (ConnectionInterface $connection) { +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; $connection->write('hello there!' . PHP_EOL); @@ -508,7 +509,7 @@ The `TcpServer` class implements the [`ServerInterface`](#serverinterface) and is responsible for accepting plaintext TCP/IP connections. ```php -$server = new TcpServer(8080, $loop); +$server = new React\Socket\TcpServer(8080, $loop); ``` As above, the `$uri` parameter can consist of only a port, in which case the @@ -518,7 +519,7 @@ which means it will not be reachable from outside of this system. In order to use a random port assignment, you can use the port `0`: ```php -$server = new TcpServer(0, $loop); +$server = new React\Socket\TcpServer(0, $loop); $address = $server->getAddress(); ``` @@ -527,14 +528,14 @@ address through the first parameter provided to the constructor, optionally preceded by the `tcp://` scheme: ```php -$server = new TcpServer('192.168.0.1:8080', $loop); +$server = new React\Socket\TcpServer('192.168.0.1:8080', $loop); ``` If you want to listen on an IPv6 address, you MUST enclose the host in square brackets: ```php -$server = new TcpServer('[::1]:8080', $loop); +$server = new React\Socket\TcpServer('[::1]:8080', $loop); ``` If the given URI is invalid, does not contain a port, any other scheme or if it @@ -542,7 +543,7 @@ contains a hostname, it will throw an `InvalidArgumentException`: ```php // throws InvalidArgumentException due to missing port -$server = new TcpServer('127.0.0.1', $loop); +$server = new React\Socket\TcpServer('127.0.0.1', $loop); ``` If the given URI appears to be valid, but listening on it fails (such as if port @@ -550,10 +551,10 @@ is already in use or port below 1024 may require root access etc.), it will throw a `RuntimeException`: ```php -$first = new TcpServer(8080, $loop); +$first = new React\Socket\TcpServer(8080, $loop); // throws RuntimeException because port is already in use -$second = new TcpServer(8080, $loop); +$second = new React\Socket\TcpServer(8080, $loop); ``` > Note that these error conditions may vary depending on your system and/or @@ -561,18 +562,18 @@ configuration. See the exception message and code for more details about the actual error condition. -Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php) +Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php) for the underlying stream socket resource like this: ```php -$server = new TcpServer('[::1]:8080', $loop, array( +$server = new React\Socket\TcpServer('[::1]:8080', $loop, array( 'backlog' => 200, 'so_reuseport' => true, 'ipv6_v6only' => true )); ``` -> Note that available [socket context options](http://php.net/manual/en/context.socket.php), +> Note that available [socket context options](https://www.php.net/manual/en/context.socket.php), their defaults and effects of changing these may vary depending on your system and/or PHP version. Passing unknown context options has no effect. @@ -581,7 +582,7 @@ Whenever a client connects, it will emit a `connection` event with a connection instance implementing [`ConnectionInterface`](#connectioninterface): ```php -$server->on('connection', function (ConnectionInterface $connection) { +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; $connection->write('hello there!' . PHP_EOL); @@ -598,13 +599,13 @@ and is responsible for providing a secure TLS (formerly known as SSL) server. It does so by wrapping a [`TcpServer`](#tcpserver) instance which waits for plaintext TCP/IP connections and then performs a TLS handshake for each connection. -It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php), +It thus requires valid [TLS context options](https://www.php.net/manual/en/context.ssl.php), which in its most basic form may look something like this if you're using a PEM encoded certificate file: ```php -$server = new TcpServer(8000, $loop); -$server = new SecureServer($server, $loop, array( +$server = new React\Socket\TcpServer(8000, $loop); +$server = new React\Socket\SecureServer($server, $loop, array( 'local_cert' => 'server.pem' )); ``` @@ -618,8 +619,8 @@ If your private key is encrypted with a passphrase, you have to specify it like this: ```php -$server = new TcpServer(8000, $loop); -$server = new SecureServer($server, $loop, array( +$server = new React\Socket\TcpServer(8000, $loop); +$server = new React\Socket\SecureServer($server, $loop, array( 'local_cert' => 'server.pem', 'passphrase' => 'secret' )); @@ -630,14 +631,14 @@ SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you want to negotiate with the remote side: ```php -$server = new TcpServer(8000, $loop); -$server = new SecureServer($server, $loop, array( +$server = new React\Socket\TcpServer(8000, $loop); +$server = new React\Socket\SecureServer($server, $loop, array( 'local_cert' => 'server.pem', 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER )); ``` -> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php), +> Note that available [TLS context options](https://www.php.net/manual/en/context.ssl.php), their defaults and effects of changing these may vary depending on your system and/or PHP version. Passing unknown context options has no effect. @@ -646,7 +647,7 @@ Whenever a client completes the TLS handshake, it will emit a `connection` event with a connection instance implementing [`ConnectionInterface`](#connectioninterface): ```php -$server->on('connection', function (ConnectionInterface $connection) { +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL; $connection->write('hello there!' . PHP_EOL); @@ -689,7 +690,7 @@ The `UnixServer` class implements the [`ServerInterface`](#serverinterface) and is responsible for accepting connections on Unix domain sockets (UDS). ```php -$server = new UnixServer('/tmp/server.sock', $loop); +$server = new React\Socket\UnixServer('/tmp/server.sock', $loop); ``` As above, the `$uri` parameter can consist of only a socket path or socket path @@ -700,17 +701,25 @@ socket is already in use or the file not accessible etc.), it will throw a `RuntimeException`: ```php -$first = new UnixServer('/tmp/same.sock', $loop); +$first = new React\Socket\UnixServer('/tmp/same.sock', $loop); // throws RuntimeException because socket is already in use -$second = new UnixServer('/tmp/same.sock', $loop); +$second = new React\Socket\UnixServer('/tmp/same.sock', $loop); ``` +> Note that these error conditions may vary depending on your system and/or + configuration. + In particular, Zend PHP does only report "Unknown error" when the UDS path + already exists and can not be bound. You may want to check `is_file()` on the + given UDS path to report a more user-friendly error message in this case. + See the exception message and code for more details about the actual error + condition. + Whenever a client connects, it will emit a `connection` event with a connection instance implementing [`ConnectionInterface`](#connectioninterface): ```php -$server->on('connection', function (ConnectionInterface $connection) { +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { echo 'New connection' . PHP_EOL; $connection->write('hello there!' . PHP_EOL); @@ -736,8 +745,8 @@ Whenever a connection closes, it will remove this connection from the list of open connections. ```php -$server = new LimitingServer($server, 100); -$server->on('connection', function (ConnectionInterface $connection) { +$server = new React\Socket\LimitingServer($server, 100); +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { $connection->write('hello there!' . PHP_EOL); … }); @@ -751,8 +760,8 @@ is exceeded. In this case, it will emit an `error` event to inform about this and no `connection` event will be emitted. ```php -$server = new LimitingServer($server, 100); -$server->on('connection', function (ConnectionInterface $connection) { +$server = new React\Socket\LimitingServer($server, 100); +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { $connection->write('hello there!' . PHP_EOL); … }); @@ -780,8 +789,8 @@ protocols that demand immediate responses (such as a "welcome" message in an interactive chat). ```php -$server = new LimitingServer($server, 100, true); -$server->on('connection', function (ConnectionInterface $connection) { +$server = new React\Socket\LimitingServer($server, 100, true); +$server->on('connection', function (React\Socket\ConnectionInterface $connection) { $connection->write('hello there!' . PHP_EOL); … }); @@ -818,7 +827,7 @@ The interface only offers a single method: #### connect() -The `connect(string $uri): PromiseInterface` method +The `connect(string $uri): PromiseInterface` method can be used to create a streaming connection to the given remote address. It returns a [Promise](https://github.com/reactphp/promise) which either @@ -827,7 +836,7 @@ on success or rejects with an `Exception` if the connection is not successful: ```php $connector->connect('google.com:443')->then( - function (ConnectionInterface $connection) { + function (React\Socket\ConnectionInterface $connection) { // connection successfully established }, function (Exception $error) { @@ -861,9 +870,9 @@ It binds to the main event loop and can be used like this: ```php $loop = React\EventLoop\Factory::create(); -$connector = new Connector($loop); +$connector = new React\Socket\Connector($loop); -$connector->connect($uri)->then(function (ConnectionInterface $connection) { +$connector->connect($uri)->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -875,7 +884,7 @@ In order to create a plaintext TCP/IP connection, you can simply pass a host and port combination like this: ```php -$connector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) { +$connector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -890,7 +899,7 @@ In order to create a secure TLS connection, you can use the `tls://` URI scheme like this: ```php -$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $connection) { +$connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -900,7 +909,7 @@ In order to create a local Unix domain socket connection, you can use the `unix://` URI scheme like this: ```php -$connector->connect('unix:///tmp/demo.sock')->then(function (ConnectionInterface $connection) { +$connector->connect('unix:///tmp/demo.sock')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -918,6 +927,22 @@ also shares all of their features and implementation details. If you want to typehint in your higher-level protocol implementation, you SHOULD use the generic [`ConnectorInterface`](#connectorinterface) instead. +As of `v1.4.0`, the `Connector` class defaults to using the +[happy eyeballs algorithm](https://en.wikipedia.org/wiki/Happy_Eyeballs) to +automatically connect over IPv4 or IPv6 when a hostname is given. +This automatically attempts to connect using both IPv4 and IPv6 at the same time +(preferring IPv6), thus avoiding the usual problems faced by users with imperfect +IPv6 connections or setups. +If you want to revert to the old behavior of only doing an IPv4 lookup and +only attempt a single IPv4 connection, you can set up the `Connector` like this: + +```php +$connector = new React\Socket\Connector($loop, array( + 'happy_eyeballs' => false +)); +``` + +Similarly, you can also affect the default DNS behavior as follows. The `Connector` class will try to detect your system DNS settings (and uses Google's public DNS server `8.8.8.8` as a fallback if unable to determine your system settings) to resolve all public hostnames into underlying IP addresses by @@ -926,11 +951,11 @@ If you explicitly want to use a custom DNS server (such as a local DNS relay or a company wide DNS server), you can set up the `Connector` like this: ```php -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'dns' => '127.0.1.1' )); -$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) { +$connector->connect('localhost:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -940,39 +965,39 @@ If you do not want to use a DNS resolver at all and want to connect to IP addresses only, you can also set up your `Connector` like this: ```php -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'dns' => false )); -$connector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) { +$connector->connect('127.0.0.1:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); ``` -Advanced: If you need a custom DNS `Resolver` instance, you can also set up -your `Connector` like this: +Advanced: If you need a custom DNS `React\Dns\Resolver\ResolverInterface` instance, you +can also set up your `Connector` like this: ```php $dnsResolverFactory = new React\Dns\Resolver\Factory(); $resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop); -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'dns' => $resolver )); -$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) { +$connector->connect('localhost:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); ``` By default, the `tcp://` and `tls://` URI schemes will use timeout value that -repects your `default_socket_timeout` ini setting (which defaults to 60s). +respects your `default_socket_timeout` ini setting (which defaults to 60s). If you want a custom timeout value, you can simply pass this like this: ```php -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'timeout' => 10.0 )); ``` @@ -981,7 +1006,7 @@ Similarly, if you do not want to apply a timeout at all and let the operating system handle this, you can pass a boolean flag like this: ```php -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'timeout' => false )); ``` @@ -992,13 +1017,13 @@ pass boolean flags like this: ```php // only allow secure TLS connections -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'tcp' => false, 'tls' => true, 'unix' => false, )); -$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $connection) { +$connector->connect('tls://google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -1011,7 +1036,7 @@ pass arrays of context options like this: ```php // allow insecure TLS connections -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'tcp' => array( 'bindto' => '192.168.0.1:0' ), @@ -1021,7 +1046,7 @@ $connector = new Connector($loop, array( ), )); -$connector->connect('tls://localhost:443')->then(function (ConnectionInterface $connection) { +$connector->connect('tls://localhost:443')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -1032,7 +1057,7 @@ SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you want to negotiate with the remote side: ```php -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'tls' => array( 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT ) @@ -1040,8 +1065,8 @@ $connector = new Connector($loop, array( ``` > For more details about context options, please refer to the PHP documentation - about [socket context options](http://php.net/manual/en/context.socket.php) - and [SSL context options](http://php.net/manual/en/context.ssl.php). + about [socket context options](https://www.php.net/manual/en/context.socket.php) + and [SSL context options](https://www.php.net/manual/en/context.ssl.php). Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and `unix://` URI schemes. @@ -1052,13 +1077,13 @@ pass an instance implementing the `ConnectorInterface` like this: ```php $dnsResolverFactory = new React\Dns\Resolver\Factory(); $resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop); -$tcp = new DnsConnector(new TcpConnector($loop), $resolver); +$tcp = new React\Socket\HappyEyeBallsConnector($loop, new React\Socket\TcpConnector($loop), $resolver); -$tls = new SecureConnector($tcp, $loop); +$tls = new React\Socket\SecureConnector($tcp, $loop); -$unix = new UnixConnector($loop); +$unix = new React\Socket\UnixConnector($loop); -$connector = new Connector($loop, array( +$connector = new React\Socket\Connector($loop, array( 'tcp' => $tcp, 'tls' => $tls, 'unix' => $unix, @@ -1067,7 +1092,7 @@ $connector = new Connector($loop, array( 'timeout' => false, )); -$connector->connect('google.com:80')->then(function (ConnectionInterface $connection) { +$connector->connect('google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -1089,14 +1114,14 @@ $connector->connect('google.com:80')->then(function (ConnectionInterface $connec #### TcpConnector -The `React\Socket\TcpConnector` class implements the +The `TcpConnector` class implements the [`ConnectorInterface`](#connectorinterface) and allows you to create plaintext TCP/IP connections to any IP-port-combination: ```php $tcpConnector = new React\Socket\TcpConnector($loop); -$tcpConnector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) { +$tcpConnector->connect('127.0.0.1:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -1119,7 +1144,7 @@ resource, thus cancelling the pending TCP/IP connection, and reject the resulting promise. You can optionally pass additional -[socket context options](http://php.net/manual/en/context.socket.php) +[socket context options](https://www.php.net/manual/en/context.socket.php) to the constructor like this: ```php @@ -1146,6 +1171,60 @@ be used to set up the TLS peer name. This is used by the `SecureConnector` and `DnsConnector` to verify the peer name and can also be used if you want a custom TLS peer name. +#### HappyEyeBallsConnector + +The `HappyEyeBallsConnector` class implements the +[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext +TCP/IP connections to any hostname-port-combination. Internally it implements the +happy eyeballs algorithm from [`RFC6555`](https://tools.ietf.org/html/rfc6555) and +[`RFC8305`](https://tools.ietf.org/html/rfc8305) to support IPv6 and IPv4 hostnames. + +It does so by decorating a given `TcpConnector` instance so that it first +looks up the given domain name via DNS (if applicable) and then establishes the +underlying TCP/IP connection to the resolved target IP address. + +Make sure to set up your DNS resolver and underlying TCP connector like this: + +```php +$dnsResolverFactory = new React\Dns\Resolver\Factory(); +$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop); + +$dnsConnector = new React\Socket\HappyEyeBallsConnector($loop, $tcpConnector, $dns); + +$dnsConnector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { + $connection->write('...'); + $connection->end(); +}); + +$loop->run(); +``` + +See also the [examples](examples). + +Pending connection attempts can be cancelled by cancelling its pending promise like so: + +```php +$promise = $dnsConnector->connect('www.google.com:80'); + +$promise->cancel(); +``` + +Calling `cancel()` on a pending promise will cancel the underlying DNS lookups +and/or the underlying TCP/IP connection(s) and reject the resulting promise. + + +> Advanced usage: Internally, the `HappyEyeBallsConnector` relies on a `Resolver` to +look up the IP addresses for the given hostname. +It will then replace the hostname in the destination URI with this IP's and +append a `hostname` query parameter and pass this updated URI to the underlying +connector. +The Happy Eye Balls algorithm describes looking the IPv6 and IPv4 address for +the given hostname so this connector sends out two DNS lookups for the A and +AAAA records. It then uses all IP addresses (both v6 and v4) and tries to +connect to all of them with a 50ms interval in between. Alterating between IPv6 +and IPv4 addresses. When a connection is established all the other DNS lookups +and connection attempts are cancelled. + #### DnsConnector The `DnsConnector` class implements the @@ -1164,7 +1243,7 @@ $dns = $dnsResolverFactory->createCached('8.8.8.8', $loop); $dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns); -$dnsConnector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) { +$dnsConnector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write('...'); $connection->end(); }); @@ -1185,8 +1264,8 @@ $promise->cancel(); Calling `cancel()` on a pending promise will cancel the underlying DNS lookup and/or the underlying TCP/IP connection and reject the resulting promise. -> Advanced usage: Internally, the `DnsConnector` relies on a `Resolver` to -look up the IP address for the given hostname. +> Advanced usage: Internally, the `DnsConnector` relies on a `React\Dns\Resolver\ResolverInterface` +to look up the IP address for the given hostname. It will then replace the hostname in the destination URI with this IP and append a `hostname` query parameter and pass this updated URI to the underlying connector. @@ -1209,7 +1288,7 @@ stream. ```php $secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop); -$secureConnector->connect('www.google.com:443')->then(function (ConnectionInterface $connection) { +$secureConnector->connect('www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n"); ... }); @@ -1231,7 +1310,7 @@ Calling `cancel()` on a pending promise will cancel the underlying TCP/IP connection and/or the SSL/TLS negotiation and reject the resulting promise. You can optionally pass additional -[SSL context options](http://php.net/manual/en/context.ssl.php) +[SSL context options](https://www.php.net/manual/en/context.ssl.php) to the constructor like this: ```php @@ -1273,7 +1352,7 @@ underlying connection attempt if it takes too long. ```php $timeoutConnector = new React\Socket\TimeoutConnector($connector, 3.0, $loop); -$timeoutConnector->connect('google.com:80')->then(function (ConnectionInterface $connection) { +$timeoutConnector->connect('google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { // connection succeeded within 3.0 seconds }); ``` @@ -1300,7 +1379,7 @@ Unix domain socket (UDS) paths like this: ```php $connector = new React\Socket\UnixConnector($loop); -$connector->connect('/tmp/demo.sock')->then(function (ConnectionInterface $connection) { +$connector->connect('/tmp/demo.sock')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("HELLO\n"); }); @@ -1328,9 +1407,9 @@ when you want to explicitly connect to a Unix domain socket (UDS) path instead of connecting to a default address assumed by an higher-level API: ```php -$connector = new FixedUriConnector( +$connector = new React\Socket\FixedUriConnector( 'unix:///var/run/docker.sock', - new UnixConnector($loop) + new React\Socket\UnixConnector($loop) ); // destination will be ignored, actually connects to Unix domain socket @@ -1342,10 +1421,11 @@ $promise = $connector->connect('localhost:80'); The recommended way to install this library is [through Composer](https://getcomposer.org). [New to Composer?](https://getcomposer.org/doc/00-intro.md) +This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require react/socket:^0.8.12 +$ composer require react/socket:^1.4 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. @@ -1363,18 +1443,14 @@ This library does not take responsibility over these context options, so it's up to consumers of this library to take care of setting appropriate context options as described above. -All versions of PHP prior to 5.6.8 suffered from a buffering issue where reading -from a streaming TLS connection could be one `data` event behind. -This library implements a work-around to try to flush the complete incoming -data buffers on these legacy PHP versions, which has a penalty of around 10% of -throughput on all connections. -With this work-around, we have not been able to reproduce this issue anymore, -but we have seen reports of people saying this could still affect some of the -older PHP versions (`5.5.23`, `5.6.7`, and `5.6.8`). -Note that this only affects *some* higher-level streaming protocols, such as -IRC over TLS, but should not affect HTTP over TLS (HTTPS). -Further investigation of this issue is needed. -For more insights, this issue is also covered by our test suite. +PHP < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might +block with 100% CPU usage on fragmented TLS records. +We try to work around this by always consuming the complete receive +buffer at once to avoid stale data in TLS buffers. This is known to +work around high CPU usage for well-behaving peers, but this may +cause very large data chunks for high throughput scenarios. The buggy +behavior can still be triggered due to network I/O buffers or +malicious peers on affected versions, upgrading is highly recommended. PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big chunks of data over TLS streams at once. diff --git a/vendor/react/socket/composer.json b/vendor/react/socket/composer.json index cad0aef..676d3e7 100644 --- a/vendor/react/socket/composer.json +++ b/vendor/react/socket/composer.json @@ -6,15 +6,16 @@ "require": { "php": ">=5.3.0", "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "react/dns": "^0.4.13", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/stream": "^1.0 || ^0.7.1", + "react/dns": "^1.1", + "react/event-loop": "^1.0 || ^0.5", "react/promise": "^2.6.0 || ^1.2.1", - "react/promise-timer": "^1.4.0" + "react/promise-timer": "^1.4.0", + "react/stream": "^1.1" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.5 || ^6.4 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" }, "autoload": { "psr-4": { diff --git a/vendor/react/socket/examples/01-echo-server.php b/vendor/react/socket/examples/01-echo-server.php deleted file mode 100644 index 2c0be57..0000000 --- a/vendor/react/socket/examples/01-echo-server.php +++ /dev/null @@ -1,42 +0,0 @@ - array( - 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem') - ) -)); - -$server->on('connection', function (ConnectionInterface $conn) { - echo '[' . $conn->getRemoteAddress() . ' connected]' . PHP_EOL; - $conn->pipe($conn); -}); - -$server->on('error', 'printf'); - -echo 'Listening on ' . $server->getAddress() . PHP_EOL; - -$loop->run(); diff --git a/vendor/react/socket/examples/02-chat-server.php b/vendor/react/socket/examples/02-chat-server.php deleted file mode 100644 index 46439e0..0000000 --- a/vendor/react/socket/examples/02-chat-server.php +++ /dev/null @@ -1,59 +0,0 @@ - array( - 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem') - ) -)); - -$server = new LimitingServer($server, null); - -$server->on('connection', function (ConnectionInterface $client) use ($server) { - // whenever a new message comes in - $client->on('data', function ($data) use ($client, $server) { - // remove any non-word characters (just for the demo) - $data = trim(preg_replace('/[^\w\d \.\,\-\!\?]/u', '', $data)); - - // ignore empty messages - if ($data === '') { - return; - } - - // prefix with client IP and broadcast to all connected clients - $data = trim(parse_url($client->getRemoteAddress(), PHP_URL_HOST), '[]') . ': ' . $data . PHP_EOL; - foreach ($server->getConnections() as $connection) { - $connection->write($data); - } - }); -}); - -$server->on('error', 'printf'); - -echo 'Listening on ' . $server->getAddress() . PHP_EOL; - -$loop->run(); diff --git a/vendor/react/socket/examples/03-http-server.php b/vendor/react/socket/examples/03-http-server.php deleted file mode 100644 index eb6d454..0000000 --- a/vendor/react/socket/examples/03-http-server.php +++ /dev/null @@ -1,57 +0,0 @@ - array( - 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem') - ) -)); - -$server->on('connection', function (ConnectionInterface $conn) { - $conn->once('data', function () use ($conn) { - $body = "

Hello world!

\r\n"; - $conn->end("HTTP/1.1 200 OK\r\nContent-Length: " . strlen($body) . "\r\nConnection: close\r\n\r\n" . $body); - }); -}); - -$server->on('error', 'printf'); - -echo 'Listening on ' . strtr($server->getAddress(), array('tcp:' => 'http:', 'tls:' => 'https:')) . PHP_EOL; - -$loop->run(); diff --git a/vendor/react/socket/examples/11-http-client.php b/vendor/react/socket/examples/11-http-client.php deleted file mode 100644 index 2b64a43..0000000 --- a/vendor/react/socket/examples/11-http-client.php +++ /dev/null @@ -1,36 +0,0 @@ -connect($host. ':80')->then(function (ConnectionInterface $connection) use ($host) { - $connection->on('data', function ($data) { - echo $data; - }); - $connection->on('close', function () { - echo '[CLOSED]' . PHP_EOL; - }); - - $connection->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n"); -}, 'printf'); - -$loop->run(); diff --git a/vendor/react/socket/examples/12-https-client.php b/vendor/react/socket/examples/12-https-client.php deleted file mode 100644 index 6e3f279..0000000 --- a/vendor/react/socket/examples/12-https-client.php +++ /dev/null @@ -1,36 +0,0 @@ -connect('tls://' . $host . ':443')->then(function (ConnectionInterface $connection) use ($host) { - $connection->on('data', function ($data) { - echo $data; - }); - $connection->on('close', function () { - echo '[CLOSED]' . PHP_EOL; - }); - - $connection->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n"); -}, 'printf'); - -$loop->run(); diff --git a/vendor/react/socket/examples/21-netcat-client.php b/vendor/react/socket/examples/21-netcat-client.php deleted file mode 100644 index 9140e2c..0000000 --- a/vendor/react/socket/examples/21-netcat-client.php +++ /dev/null @@ -1,68 +0,0 @@ -' . PHP_EOL); - exit(1); -} - -$loop = Factory::create(); -$connector = new Connector($loop); - -$stdin = new ReadableResourceStream(STDIN, $loop); -$stdin->pause(); -$stdout = new WritableResourceStream(STDOUT, $loop); -$stderr = new WritableResourceStream(STDERR, $loop); - -$stderr->write('Connecting' . PHP_EOL); - -$connector->connect($argv[1])->then(function (ConnectionInterface $connection) use ($stdin, $stdout, $stderr) { - // pipe everything from STDIN into connection - $stdin->resume(); - $stdin->pipe($connection); - - // pipe everything from connection to STDOUT - $connection->pipe($stdout); - - // report errors to STDERR - $connection->on('error', function ($error) use ($stderr) { - $stderr->write('Stream ERROR: ' . $error . PHP_EOL); - }); - - // report closing and stop reading from input - $connection->on('close', function () use ($stderr, $stdin) { - $stderr->write('[CLOSED]' . PHP_EOL); - $stdin->close(); - }); - - $stderr->write('Connected' . PHP_EOL); -}, function ($error) use ($stderr) { - $stderr->write('Connection ERROR: ' . $error . PHP_EOL); -}); - -$loop->run(); diff --git a/vendor/react/socket/examples/22-http-client.php b/vendor/react/socket/examples/22-http-client.php deleted file mode 100644 index fcb8107..0000000 --- a/vendor/react/socket/examples/22-http-client.php +++ /dev/null @@ -1,60 +0,0 @@ -' . PHP_EOL); - exit(1); -} - -$loop = Factory::create(); -$connector = new Connector($loop); - -if (!isset($parts['port'])) { - $parts['port'] = $parts['scheme'] === 'https' ? 443 : 80; -} - -$host = $parts['host']; -if (($parts['scheme'] === 'http' && $parts['port'] !== 80) || ($parts['scheme'] === 'https' && $parts['port'] !== 443)) { - $host .= ':' . $parts['port']; -} -$target = ($parts['scheme'] === 'https' ? 'tls' : 'tcp') . '://' . $parts['host'] . ':' . $parts['port']; -$resource = isset($parts['path']) ? $parts['path'] : '/'; -if (isset($parts['query'])) { - $resource .= '?' . $parts['query']; -} - -$stdout = new WritableResourceStream(STDOUT, $loop); - -$connector->connect($target)->then(function (ConnectionInterface $connection) use ($resource, $host, $stdout) { - $connection->pipe($stdout); - - $connection->write("GET $resource HTTP/1.0\r\nHost: $host\r\n\r\n"); -}, 'printf'); - -$loop->run(); diff --git a/vendor/react/socket/examples/91-benchmark-server.php b/vendor/react/socket/examples/91-benchmark-server.php deleted file mode 100644 index 420d474..0000000 --- a/vendor/react/socket/examples/91-benchmark-server.php +++ /dev/null @@ -1,60 +0,0 @@ - array( - 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem') - ) -)); - -$server->on('connection', function (ConnectionInterface $conn) use ($loop) { - echo '[connected]' . PHP_EOL; - - // count the number of bytes received from this connection - $bytes = 0; - $conn->on('data', function ($chunk) use (&$bytes) { - $bytes += strlen($chunk); - }); - - // report average throughput once client disconnects - $t = microtime(true); - $conn->on('close', function () use ($conn, $t, &$bytes) { - $t = microtime(true) - $t; - echo '[disconnected after receiving ' . $bytes . ' bytes in ' . round($t, 3) . 's => ' . round($bytes / $t / 1024 / 1024, 1) . ' MiB/s]' . PHP_EOL; - }); -}); - -$server->on('error', 'printf'); - -echo 'Listening on ' . $server->getAddress() . PHP_EOL; - -$loop->run(); diff --git a/vendor/react/socket/examples/99-generate-self-signed.php b/vendor/react/socket/examples/99-generate-self-signed.php deleted file mode 100644 index 00f9314..0000000 --- a/vendor/react/socket/examples/99-generate-self-signed.php +++ /dev/null @@ -1,31 +0,0 @@ - secret.pem - -// certificate details (Distinguished Name) -// (OpenSSL applies defaults to missing fields) -$dn = array( - "commonName" => isset($argv[1]) ? $argv[1] : "localhost", -// "countryName" => "AU", -// "stateOrProvinceName" => "Some-State", -// "localityName" => "London", -// "organizationName" => "Internet Widgits Pty Ltd", -// "organizationalUnitName" => "R&D", -// "emailAddress" => "admin@example.com" -); - -// create certificate which is valid for ~10 years -$privkey = openssl_pkey_new(); -$cert = openssl_csr_new($dn, $privkey); -$cert = openssl_csr_sign($cert, null, $privkey, 3650); - -// export public and (optionally encrypted) private key in PEM format -openssl_x509_export($cert, $out); -echo $out; - -$passphrase = isset($argv[2]) ? $argv[2] : null; -openssl_pkey_export($privkey, $out, $passphrase); -echo $out; diff --git a/vendor/react/socket/examples/localhost.pem b/vendor/react/socket/examples/localhost.pem deleted file mode 100644 index be69279..0000000 --- a/vendor/react/socket/examples/localhost.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu -MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK -DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQ1OTA2WhcNMjYx -MjI4MTQ1OTA2WjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw -EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 -eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8SZWNS+Ktg0Py -W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN -2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9 -zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0 -UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4 -wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY -YCUE54G/AgMBAAGjUDBOMB0GA1UdDgQWBBQ2GRz3QsQzdXaTMnPVCKfpigA10DAf -BgNVHSMEGDAWgBQ2GRz3QsQzdXaTMnPVCKfpigA10DAMBgNVHRMEBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQA77iZ4KrpPY18Ezjt0mngYAuAxunKddXYdLZ2khywN -0uI/VzYnkFVtrsC7y2jLHSxlmE2/viPPGZDUplENV2acN6JNW+tlt7/bsrQHDQw3 -7VCF27EWiDxHsaghhLkqC+kcop5YR5c0oDQTdEWEKSbow2zayUXDYbRRs76SClTe -824Yul+Ts8Mka+AX2PXDg47iZ84fJRN/nKavcJUTJ2iS1uYw0GNnFMge/uwsfMR3 -V47qN0X5emky8fcq99FlMCbcy0gHAeSWAjClgr2dd2i0LDatUbj7YmdmFcskOgII -IwGfvuWR2yPevYGAE0QgFeLHniN3RW8zmpnX/XtrJ4a7 ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8SZWNS+Ktg0Py -W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN -2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9 -zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0 -UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4 -wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY -YCUE54G/AgMBAAECggEBAKiO/3FE1CMddkCLZVtUp8ShqJgRokx9WI5ecwFApAkV -ZHsjqDQQYRNmxhDUX/w0tOzLGyhde2xjJyZG29YviKsbHwu6zYwbeOzy/mkGOaK/ -g6DmmMmRs9Z6juifoQCu4GIFZ6il2adIL2vF7OeJh+eKudQj/7NFRSB7mXzNrQWK -tZY3eux5zXWmio7pgZrx1HFZQiiL9NVLwT9J7oBnaoO3fREiu5J2xBpljG9Cr0j1 -LLiVLhukWJYRlHDtGt1CzI9w8iKo44PCRzpKyxpbsOrQxeSyEWUYQRv9VHA59LC7 -tVAJTbnTX1BNHkGZkOkoOpoZLwBaM2XbbDtcOGCAZMECgYEA+mTURFQ85/pxawvk -9ndqZ+5He1u/bMLYIJDp0hdB/vgD+vw3gb2UyRwp0I6Wc6Si4FEEnbY7L0pzWsiR -43CpLs+cyLfnD9NycuIasxs5fKb/1s1nGTkRAp7x9x/ZTtEf8v4YTmmMXFHzdo7V -pv+czO89ppEDkxEtMf/b5SifhO8CgYEAwIDIUvXLduGhL+RPDwjc2SKdydXGV6om -OEdt/V8oS801Z7k8l3gHXFm7zL/MpHmh9cag+F9dHK42kw2RSjDGsBlXXiAO1Z0I -2A34OdPw/kow8fmIKWTMu3+28Kca+3RmUqeyaq0vazQ/bWMO9px+Ud3YfLo1Tn5I -li0MecAx8DECgYEAvsLceKYYtL83c09fg2oc1ctSCCgw4WJcGAtvJ9DyRZacKbXH -b/+H/+OF8879zmKqd+0hcCnqUzAMTCisBLPLIM+o6b45ufPkqKObpcJi/JWaKgLY -vf2c+Psw6o4IF6T5Cz4MNIjzF06UBknxecYZpoPJ20F1kLCwVvxPgfl99l8CgYAb -XfOcv67WTstgiJ+oroTfJamy+P5ClkDqvVTosW+EHz9ZaJ8xlXHOcj9do2LPey9I -Rp250azmF+pQS5x9JKQKgv/FtN8HBVUtigbhCb14GUoODICMCfWFLmnumoMefnTR -iV+3BLn6Dqp5vZxx+NuIffZ5/Or5JsDhALSGVomC8QKBgAi3Z/dNQrDHfkXMNn/L -+EAoLuAbFgLs76r9VGgNaRQ/q5gex2bZEGoBj4Sxvs95NUIcfD9wKT7FF8HdxARv -y3o6Bfc8Xp9So9SlFXrje+gkdEJ0rQR67d+XBuJZh86bXJHVrMwpoNL+ahLGdVSe -81oh1uCH1YPLM29hPyaohxL8 ------END PRIVATE KEY----- diff --git a/vendor/react/socket/examples/localhost_swordfish.pem b/vendor/react/socket/examples/localhost_swordfish.pem deleted file mode 100644 index 7d1ee80..0000000 --- a/vendor/react/socket/examples/localhost_swordfish.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu -MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK -DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQxMDQzWhcNMjYx -MjI4MTQxMDQzWjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw -EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 -eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRXt83SrKIHr/i -3lc8O8pz6NHE1DNHJa4xg2xalXWzCEV6m1qLd9VdaLT9cJD1afNmEMBgY6RblNL/ -paJWVoR9MOUeIoYl2PrhUCxsf7h6MRtezQQe3e+n+/0XunF0JUQIZuJqbxfRk5WT -XmYnphqOZKEcistAYvFBjzl/D+Cl/nYsreADc+t9l5Vni89oTWEuqIrsM4WUZqqB -VMAakd2nZJLWIrMxq9hbW1XNukOQfcmZVFTC6CUnLq8qzGbtfZYBuMBACnL1k/E/ -yPaAgR46l14VAcndDUJBtMeL2qYuNwvXQhg3KuBmpTUpH+yzxU+4T3lmv0xXmPqu -ySH3xvW3AgMBAAGjUDBOMB0GA1UdDgQWBBRu68WTI4pVeTB7wuG9QGI3Ie441TAf -BgNVHSMEGDAWgBRu68WTI4pVeTB7wuG9QGI3Ie441TAMBgNVHRMEBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQCc4pEjEHO47VRJkbHgC+c2gAVgxekkaA1czBA1uAvh -ILRda0NLlvyftbjaG0zZp2ABUCfRfksl/Pf/PzWLUMEuH/9kEW2rgP43z6YgiL6k -kBPlmAU607UjD726RPGkw8QPSXS/dWiNJ5CBpPWLpxC45pokqItYbY0ijQ5Piq09 -TchYlCX044oSRnPiP394PQ3HVdaGhJB2DnjDq3in5dVivFf8EdgzQSvp/wXy3WQs -uFSVonSnrZGY/4AgT3psGaQ6fqKb4SBoqtf5bFQvp1XNNRkuEJnS/0dygEya0c+c -aCe/1gXC2wDjx0/TekY5m1Nyw5SY6z7stOqL/ekwgejt ------END CERTIFICATE----- ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIG7idPRLgiHkCAggA -MBQGCCqGSIb3DQMHBAg+MLPdepHWSwSCBMgVW9LseCjfTAmF9U1qRnKsq3kIwEnW -6aERBqs/mnmEhrXgZYgcvRRK7kD12TdHt/Nz46Ymu0h+Lrvuwtl1fHQUARTk/gFh -onLhc9kjMUhLRIR007vJe3HvWOb/v+SBSDB38OpUxUwJmBVBuSaYLWVuPR6J5kUj -xOgBS049lN3E9cfrHvb3bF/epIQrU0OgfyyxEvIi5n30y+tlRn3y68PY6Qd46t4Y -UN5VZUwvJBgoRy9TGxSkiSRjhxC2PWpLYq/HMzDbcRcFF5dVAIioUd/VZ7fdgBfA -uMW4SFfpFLDUX0aaYe+ZdA5tM0Bc0cOtG8Z0sc9JYDNNcvjmSiGCi646h8F0D3O6 -JKAQMMxQGWiyQeJ979LVjtq4lJESXA8VEKz9rV03y5xunmFCLy6dGt+6GJwXgabn -OH7nvEv4GqAOqKc6E9je4JM+AF/oUazrfPse1KEEtsPKarazjCB/SKYtHyDJaavD -GGjtiU9zWwGMOgIDyNmXe3ga7/TWoGOAg5YlTr6Hbq2Y/5ycgjAgPFjuXtvnoT0+ -mF5TnNfMAqTgQsE2gjhonK1pdlOen0lN5FtoUXp3CXU0dOq0J70GiX+1YA7VDn30 -n5WNAgfOXX3l3E95jGN370pHXyli5RUNW0NZVHV+22jlNWCVtQHUh+DVswQZg+i5 -+DqaIHz2jUetMo7gWtqGn/wwSopOs87VM1rcALhZL4EsJ+Zy81I/hA32RNnGbuol -NAiZh+0KrtTcc/fPunpd8vRtOwGphM11dKucozUufuiPG2inR3aEqt5yNx54ec/f -J6nryWRYiHEA/rCU9MSBM9cqKFtEmy9/8oxV41/SPxhXjHwDlABWTtFuJ3pf2sOF -ILSYYFwB0ZGvdjE5yAJFBr9efno/L9fafmGk7a3vmVgK2AmUC9VNB5XHw1GjF8OP -aQAXe4md9Bh0jk/D/iyp7e7IWNssul/7XejhabidWgFj6EXc9YxE59+FlhDqyMhn -V6houc+QeUXuwsAKgRJJhJtpv/QSZ5BI3esxHHUt3ayGnvhFElpAc0t7C/EiXKIv -DAFYP2jksBqijM8YtEgPWYzEP5buYxZnf/LK7FDocLsNcdF38UaKBbeF90e7bR8j -SHspG9aJWICu8Yawnh8zuy/vQv+h9gWyGodd2p9lQzlbRXrutbwfmPf7xP6nzT9i -9GcugJxTaZgkCfhhHxFk/nRHS2NAzagKVib1xkUlZJg2hX0fIFUdYteL1GGTvOx5 -m3mTOino4T19z9SEdZYb2OHYh29e/T74bJiLCYdXwevSYHxfZc8pYAf0jp4UnMT2 -f7B0ctX1iXuQ2uZVuxh+U1Mcu+v0gDla1jWh7AhcePSi4xBNUCak0kQip6r5e6Oi -r4MIyMRk/Pc5pzEKo8G6nk26rNvX3aRvECoVfmK7IVdsqZ6IXlt9kOmWx3IeKzrO -J5DxpzW+9oIRZJgPTkc4/XRb0tFmFQYTiChiQ1AJUEiCX0GpkFf7cq61aLGYtWyn -vL2lmQhljzjrDo15hKErvk7eBZW7GW/6j/m/PfRdcBI4ceuP9zWQXnDOd9zmaE4b -q3bJ+IbbyVZA2WwyzN7umCKWghsiPMAolxEnYM9JRf8BcqeqQiwVZlfO5KFuN6Ze -le4= ------END ENCRYPTED PRIVATE KEY----- diff --git a/vendor/react/socket/phpunit.xml.dist b/vendor/react/socket/phpunit.xml.dist deleted file mode 100644 index 13d3fab..0000000 --- a/vendor/react/socket/phpunit.xml.dist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - ./tests/ - - - - - - ./src/ - - - diff --git a/vendor/react/socket/src/Connection.php b/vendor/react/socket/src/Connection.php index c6267cc..5da20c9 100644 --- a/vendor/react/socket/src/Connection.php +++ b/vendor/react/socket/src/Connection.php @@ -43,14 +43,16 @@ class Connection extends EventEmitter implements ConnectionInterface public function __construct($resource, LoopInterface $loop) { - // PHP < 5.6.8 suffers from a buffer indicator bug on secure TLS connections - // as a work-around we always read the complete buffer until its end. - // The buffer size is limited due to TCP/IP buffers anyway, so this - // should not affect usage otherwise. - // See https://bugs.php.net/bug.php?id=65137 - // https://bugs.php.net/bug.php?id=41631 - // https://github.com/reactphp/socket-client/issues/24 - $clearCompleteBuffer = PHP_VERSION_ID < 50608; + // PHP < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might + // block with 100% CPU usage on fragmented TLS records. + // We try to work around this by always consuming the complete receive + // buffer at once to avoid stale data in TLS buffers. This is known to + // work around high CPU usage for well-behaving peers, but this may + // cause very large data chunks for high throughput scenarios. The buggy + // behavior can still be triggered due to network I/O buffers or + // malicious peers on affected versions, upgrading is highly recommended. + // @link https://bugs.php.net/bug.php?id=77390 + $clearCompleteBuffer = \PHP_VERSION_ID < 70215 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70303); // PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big // chunks of data over TLS streams at once. @@ -60,7 +62,7 @@ public function __construct($resource, LoopInterface $loop) // affected versions. Please update your PHP version. // This applies to all streams because TLS may be enabled later on. // See https://github.com/reactphp/socket/issues/105 - $limitWriteChunks = (PHP_VERSION_ID < 70018 || (PHP_VERSION_ID >= 70100 && PHP_VERSION_ID < 70104)); + $limitWriteChunks = (\PHP_VERSION_ID < 70018 || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70104)); $this->input = new DuplexResourceStream( $resource, @@ -120,7 +122,7 @@ public function close() public function handleClose() { - if (!is_resource($this->stream)) { + if (!\is_resource($this->stream)) { return; } @@ -130,18 +132,18 @@ public function handleClose() // continuing to close the socket resource. // Underlying Stream implementation will take care of closing file // handle, so we otherwise keep this open here. - @stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR); - stream_set_blocking($this->stream, false); + @\stream_socket_shutdown($this->stream, \STREAM_SHUT_RDWR); + \stream_set_blocking($this->stream, false); } public function getRemoteAddress() { - return $this->parseAddress(@stream_socket_get_name($this->stream, true)); + return $this->parseAddress(@\stream_socket_get_name($this->stream, true)); } public function getLocalAddress() { - return $this->parseAddress(@stream_socket_get_name($this->stream, false)); + return $this->parseAddress(@\stream_socket_get_name($this->stream, false)); } private function parseAddress($address) @@ -153,8 +155,8 @@ private function parseAddress($address) if ($this->unix) { // remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo // note that technically ":" is a valid address, so keep this in place otherwise - if (substr($address, -1) === ':' && defined('HHVM_VERSION_ID') && HHVM_VERSION_ID < 31900) { - $address = (string)substr($address, 0, -1); + if (\substr($address, -1) === ':' && \defined('HHVM_VERSION_ID') && \HHVM_VERSION_ID < 31900) { + $address = (string)\substr($address, 0, -1); } // work around unknown addresses should return null value: https://3v4l.org/5C1lo and https://bugs.php.net/bug.php?id=74556 @@ -167,10 +169,10 @@ private function parseAddress($address) } // check if this is an IPv6 address which includes multiple colons but no square brackets - $pos = strrpos($address, ':'); - if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') { - $port = substr($address, $pos + 1); - $address = '[' . substr($address, 0, $pos) . ']:' . $port; + $pos = \strrpos($address, ':'); + if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') { + $port = \substr($address, $pos + 1); + $address = '[' . \substr($address, 0, $pos) . ']:' . $port; } return ($this->encryptionEnabled ? 'tls' : 'tcp') . '://' . $address; diff --git a/vendor/react/socket/src/Connector.php b/vendor/react/socket/src/Connector.php index 75276bc..0225f0f 100644 --- a/vendor/react/socket/src/Connector.php +++ b/vendor/react/socket/src/Connector.php @@ -2,12 +2,10 @@ namespace React\Socket; -use React\Dns\Config\Config; -use React\Dns\Resolver\Factory; -use React\Dns\Resolver\Resolver; +use React\Dns\Config\Config as DnsConfig; +use React\Dns\Resolver\Factory as DnsFactory; +use React\Dns\Resolver\ResolverInterface; use React\EventLoop\LoopInterface; -use React\Promise; -use RuntimeException; /** * The `Connector` class is the main class in this package that implements the @@ -38,10 +36,11 @@ public function __construct(LoopInterface $loop, array $options = array()) 'dns' => true, 'timeout' => true, + 'happy_eyeballs' => true, ); if ($options['timeout'] === true) { - $options['timeout'] = (float)ini_get("default_socket_timeout"); + $options['timeout'] = (float)\ini_get("default_socket_timeout"); } if ($options['tcp'] instanceof ConnectorInterface) { @@ -49,30 +48,34 @@ public function __construct(LoopInterface $loop, array $options = array()) } else { $tcp = new TcpConnector( $loop, - is_array($options['tcp']) ? $options['tcp'] : array() + \is_array($options['tcp']) ? $options['tcp'] : array() ); } if ($options['dns'] !== false) { - if ($options['dns'] instanceof Resolver) { + if ($options['dns'] instanceof ResolverInterface) { $resolver = $options['dns']; } else { if ($options['dns'] !== true) { $server = $options['dns']; } else { // try to load nameservers from system config or default to Google's public DNS - $config = Config::loadSystemConfigBlocking(); - $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8'; + $config = DnsConfig::loadSystemConfigBlocking(); + $server = $config->nameservers ? \reset($config->nameservers) : '8.8.8.8'; } - $factory = new Factory(); - $resolver = $factory->create( + $factory = new DnsFactory(); + $resolver = $factory->createCached( $server, $loop ); } - $tcp = new DnsConnector($tcp, $resolver); + if ($options['happy_eyeballs'] === true) { + $tcp = new HappyEyeBallsConnector($loop, $tcp, $resolver); + } else { + $tcp = new DnsConnector($tcp, $resolver); + } } if ($options['tcp'] !== false) { @@ -94,7 +97,7 @@ public function __construct(LoopInterface $loop, array $options = array()) $options['tls'] = new SecureConnector( $tcp, $loop, - is_array($options['tls']) ? $options['tls'] : array() + \is_array($options['tls']) ? $options['tls'] : array() ); } @@ -120,12 +123,12 @@ public function __construct(LoopInterface $loop, array $options = array()) public function connect($uri) { $scheme = 'tcp'; - if (strpos($uri, '://') !== false) { - $scheme = (string)substr($uri, 0, strpos($uri, '://')); + if (\strpos($uri, '://') !== false) { + $scheme = (string)\substr($uri, 0, \strpos($uri, '://')); } if (!isset($this->connectors[$scheme])) { - return Promise\reject(new RuntimeException( + return \React\Promise\reject(new \RuntimeException( 'No connector available for URI scheme "' . $scheme . '"' )); } diff --git a/vendor/react/socket/src/ConnectorInterface.php b/vendor/react/socket/src/ConnectorInterface.php index 196d01a..3dd78f1 100644 --- a/vendor/react/socket/src/ConnectorInterface.php +++ b/vendor/react/socket/src/ConnectorInterface.php @@ -30,7 +30,7 @@ interface ConnectorInterface * * ```php * $connector->connect('google.com:443')->then( - * function (ConnectionInterface $connection) { + * function (React\Socket\ConnectionInterface $connection) { * // connection successfully established * }, * function (Exception $error) { diff --git a/vendor/react/socket/src/DnsConnector.php b/vendor/react/socket/src/DnsConnector.php index 0dfd658..9d0341b 100644 --- a/vendor/react/socket/src/DnsConnector.php +++ b/vendor/react/socket/src/DnsConnector.php @@ -2,18 +2,16 @@ namespace React\Socket; -use React\Dns\Resolver\Resolver; +use React\Dns\Resolver\ResolverInterface; use React\Promise; use React\Promise\CancellablePromiseInterface; -use InvalidArgumentException; -use RuntimeException; final class DnsConnector implements ConnectorInterface { private $connector; private $resolver; - public function __construct(ConnectorInterface $connector, Resolver $resolver) + public function __construct(ConnectorInterface $connector, ResolverInterface $resolver) { $this->connector = $connector; $this->resolver = $resolver; @@ -21,90 +19,95 @@ public function __construct(ConnectorInterface $connector, Resolver $resolver) public function connect($uri) { - if (strpos($uri, '://') === false) { - $parts = parse_url('tcp://' . $uri); + if (\strpos($uri, '://') === false) { + $parts = \parse_url('tcp://' . $uri); unset($parts['scheme']); } else { - $parts = parse_url($uri); + $parts = \parse_url($uri); } if (!$parts || !isset($parts['host'])) { - return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid')); + return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid')); } - $host = trim($parts['host'], '[]'); + $host = \trim($parts['host'], '[]'); $connector = $this->connector; // skip DNS lookup / URI manipulation if this URI already contains an IP - if (false !== filter_var($host, FILTER_VALIDATE_IP)) { + if (false !== \filter_var($host, \FILTER_VALIDATE_IP)) { return $connector->connect($uri); } - return $this - ->resolveHostname($host) - ->then(function ($ip) use ($connector, $host, $parts) { - $uri = ''; - - // prepend original scheme if known - if (isset($parts['scheme'])) { - $uri .= $parts['scheme'] . '://'; - } - - if (strpos($ip, ':') !== false) { - // enclose IPv6 addresses in square brackets before appending port - $uri .= '[' . $ip . ']'; - } else { - $uri .= $ip; - } - - // append original port if known - if (isset($parts['port'])) { - $uri .= ':' . $parts['port']; - } - - // append orignal path if known - if (isset($parts['path'])) { - $uri .= $parts['path']; - } - - // append original query if known - if (isset($parts['query'])) { - $uri .= '?' . $parts['query']; - } - - // append original hostname as query if resolved via DNS and if - // destination URI does not contain "hostname" query param already - $args = array(); - parse_str(isset($parts['query']) ? $parts['query'] : '', $args); - if ($host !== $ip && !isset($args['hostname'])) { - $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . rawurlencode($host); - } - - // append original fragment if known - if (isset($parts['fragment'])) { - $uri .= '#' . $parts['fragment']; - } - - return $connector->connect($uri); - }); - } - - private function resolveHostname($host) - { $promise = $this->resolver->resolve($host); + $resolved = null; return new Promise\Promise( - function ($resolve, $reject) use ($promise) { + function ($resolve, $reject) use (&$promise, &$resolved, $uri, $connector, $host, $parts) { // resolve/reject with result of DNS lookup - $promise->then($resolve, $reject); + $promise->then(function ($ip) use (&$promise, &$resolved, $connector, $host, $parts) { + $resolved = $ip; + $uri = ''; + + // prepend original scheme if known + if (isset($parts['scheme'])) { + $uri .= $parts['scheme'] . '://'; + } + + if (\strpos($ip, ':') !== false) { + // enclose IPv6 addresses in square brackets before appending port + $uri .= '[' . $ip . ']'; + } else { + $uri .= $ip; + } + + // append original port if known + if (isset($parts['port'])) { + $uri .= ':' . $parts['port']; + } + + // append orignal path if known + if (isset($parts['path'])) { + $uri .= $parts['path']; + } + + // append original query if known + if (isset($parts['query'])) { + $uri .= '?' . $parts['query']; + } + + // append original hostname as query if resolved via DNS and if + // destination URI does not contain "hostname" query param already + $args = array(); + \parse_str(isset($parts['query']) ? $parts['query'] : '', $args); + if ($host !== $ip && !isset($args['hostname'])) { + $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . \rawurlencode($host); + } + + // append original fragment if known + if (isset($parts['fragment'])) { + $uri .= '#' . $parts['fragment']; + } + + return $promise = $connector->connect($uri); + }, function ($e) use ($uri, $reject) { + $reject(new \RuntimeException('Connection to ' . $uri .' failed during DNS lookup: ' . $e->getMessage(), 0, $e)); + })->then($resolve, $reject); }, - function ($_, $reject) use ($promise) { + function ($_, $reject) use (&$promise, &$resolved, $uri) { // cancellation should reject connection attempt - $reject(new RuntimeException('Connection attempt cancelled during DNS lookup')); + // reject DNS resolution with custom reason, otherwise rely on connection cancellation below + if ($resolved === null) { + $reject(new \RuntimeException('Connection to ' . $uri . ' cancelled during DNS lookup')); + } - // (try to) cancel pending DNS lookup + // (try to) cancel pending DNS lookup / connection attempt if ($promise instanceof CancellablePromiseInterface) { + // overwrite callback arguments for PHP7+ only, so they do not show + // up in the Exception trace and do not cause a possible cyclic reference. + $_ = $reject = null; + $promise->cancel(); + $promise = null; } } ); diff --git a/vendor/react/socket/src/FixedUriConnector.php b/vendor/react/socket/src/FixedUriConnector.php index 057bcdf..9317eee 100644 --- a/vendor/react/socket/src/FixedUriConnector.php +++ b/vendor/react/socket/src/FixedUriConnector.php @@ -10,9 +10,9 @@ * instead of connecting to a default address assumed by an higher-level API: * * ```php - * $connector = new FixedUriConnector( + * $connector = new React\Socket\FixedUriConnector( * 'unix:///var/run/docker.sock', - * new UnixConnector($loop) + * new React\Socket\UnixConnector($loop) * ); * * // destination will be ignored, actually connects to Unix domain socket diff --git a/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php b/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php new file mode 100644 index 0000000..57d6150 --- /dev/null +++ b/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php @@ -0,0 +1,296 @@ + false, + Message::TYPE_AAAA => false, + ); + public $resolverPromises = array(); + public $connectionPromises = array(); + public $connectQueue = array(); + public $nextAttemptTimer; + public $parts; + public $ipsCount = 0; + public $failureCount = 0; + public $resolve; + public $reject; + + public function __construct(LoopInterface $loop, ConnectorInterface $connector, ResolverInterface $resolver, $uri, $host, $parts) + { + $this->loop = $loop; + $this->connector = $connector; + $this->resolver = $resolver; + $this->uri = $uri; + $this->host = $host; + $this->parts = $parts; + } + + public function connect() + { + $timer = null; + $that = $this; + return new Promise\Promise(function ($resolve, $reject) use ($that, &$timer) { + $lookupResolve = function ($type) use ($that, $resolve, $reject) { + return function (array $ips) use ($that, $type, $resolve, $reject) { + unset($that->resolverPromises[$type]); + $that->resolved[$type] = true; + + $that->mixIpsIntoConnectQueue($ips); + + if ($that->nextAttemptTimer instanceof TimerInterface) { + return; + } + + $that->check($resolve, $reject); + }; + }; + + $that->resolverPromises[Message::TYPE_AAAA] = $that->resolve(Message::TYPE_AAAA, $reject)->then($lookupResolve(Message::TYPE_AAAA)); + $that->resolverPromises[Message::TYPE_A] = $that->resolve(Message::TYPE_A, $reject)->then(function ($ips) use ($that, &$timer) { + // happy path: IPv6 has resolved already, continue with IPv4 addresses + if ($that->resolved[Message::TYPE_AAAA] === true) { + return $ips; + } + + // Otherwise delay processing IPv4 lookup until short timer passes or IPv6 resolves in the meantime + $deferred = new Promise\Deferred(); + $timer = $that->loop->addTimer($that::RESOLUTION_DELAY, function () use ($deferred, $ips) { + $deferred->resolve($ips); + }); + + $that->resolverPromises[Message::TYPE_AAAA]->then(function () use ($that, $timer, $deferred, $ips) { + $that->loop->cancelTimer($timer); + $deferred->resolve($ips); + }); + + return $deferred->promise(); + })->then($lookupResolve(Message::TYPE_A)); + }, function ($_, $reject) use ($that, &$timer) { + $reject(new \RuntimeException('Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : ''))); + $_ = $reject = null; + + $that->cleanUp(); + if ($timer instanceof TimerInterface) { + $that->loop->cancelTimer($timer); + } + }); + } + + /** + * @internal + */ + public function resolve($type, $reject) + { + $that = $this; + return $that->resolver->resolveAll($that->host, $type)->then(null, function () use ($type, $reject, $that) { + unset($that->resolverPromises[$type]); + $that->resolved[$type] = true; + + if ($that->hasBeenResolved() === false) { + return; + } + + if ($that->ipsCount === 0) { + $that->resolverPromises = null; + $reject(new \RuntimeException('Connection to ' . $that->uri . ' failed during DNS lookup: DNS error')); + } + }); + } + + /** + * @internal + */ + public function check($resolve, $reject) + { + if (\count($this->connectQueue) === 0 && $this->resolved[Message::TYPE_A] === true && $this->resolved[Message::TYPE_AAAA] === true && $this->nextAttemptTimer instanceof TimerInterface) { + $this->loop->cancelTimer($this->nextAttemptTimer); + $this->nextAttemptTimer = null; + } + + if (\count($this->connectQueue) === 0) { + return; + } + + $ip = \array_shift($this->connectQueue); + + $that = $this; + $that->connectionPromises[$ip] = $this->attemptConnection($ip)->then(function ($connection) use ($that, $ip, $resolve) { + unset($that->connectionPromises[$ip]); + + $that->cleanUp(); + + $resolve($connection); + }, function () use ($that, $ip, $reject) { + unset($that->connectionPromises[$ip]); + + $that->failureCount++; + + if ($that->hasBeenResolved() === false) { + return; + } + + if ($that->ipsCount === $that->failureCount) { + $that->cleanUp(); + + $reject(new \RuntimeException('All attempts to connect to "' . $that->host . '" have failed')); + } + }); + + /** + * As long as we haven't connected yet keep popping an IP address of the connect queue until one of them + * succeeds or they all fail. We will wait 100ms between connection attempts as per RFC. + * + * @link https://tools.ietf.org/html/rfc8305#section-5 + */ + if ((\count($this->connectQueue) > 0 || ($this->resolved[Message::TYPE_A] === false || $this->resolved[Message::TYPE_AAAA] === false)) && $this->nextAttemptTimer === null) { + $this->nextAttemptTimer = $this->loop->addPeriodicTimer(self::CONNECTION_ATTEMPT_DELAY, function () use ($that, $resolve, $reject) { + $that->check($resolve, $reject); + }); + } + } + + /** + * @internal + */ + public function attemptConnection($ip) + { + $uri = ''; + + // prepend original scheme if known + if (isset($this->parts['scheme'])) { + $uri .= $this->parts['scheme'] . '://'; + } + + if (\strpos($ip, ':') !== false) { + // enclose IPv6 addresses in square brackets before appending port + $uri .= '[' . $ip . ']'; + } else { + $uri .= $ip; + } + + // append original port if known + if (isset($this->parts['port'])) { + $uri .= ':' . $this->parts['port']; + } + + // append orignal path if known + if (isset($this->parts['path'])) { + $uri .= $this->parts['path']; + } + + // append original query if known + if (isset($this->parts['query'])) { + $uri .= '?' . $this->parts['query']; + } + + // append original hostname as query if resolved via DNS and if + // destination URI does not contain "hostname" query param already + $args = array(); + \parse_str(isset($this->parts['query']) ? $this->parts['query'] : '', $args); + if ($this->host !== $ip && !isset($args['hostname'])) { + $uri .= (isset($this->parts['query']) ? '&' : '?') . 'hostname=' . \rawurlencode($this->host); + } + + // append original fragment if known + if (isset($this->parts['fragment'])) { + $uri .= '#' . $this->parts['fragment']; + } + + return $this->connector->connect($uri); + } + + /** + * @internal + */ + public function cleanUp() + { + foreach ($this->connectionPromises as $connectionPromise) { + if ($connectionPromise instanceof CancellablePromiseInterface) { + $connectionPromise->cancel(); + } + } + + foreach ($this->resolverPromises as $resolverPromise) { + if ($resolverPromise instanceof CancellablePromiseInterface) { + $resolverPromise->cancel(); + } + } + + if ($this->nextAttemptTimer instanceof TimerInterface) { + $this->loop->cancelTimer($this->nextAttemptTimer); + $this->nextAttemptTimer = null; + } + } + + /** + * @internal + */ + public function hasBeenResolved() + { + foreach ($this->resolved as $typeHasBeenResolved) { + if ($typeHasBeenResolved === false) { + return false; + } + } + + return true; + } + + /** + * Mixes an array of IP addresses into the connect queue in such a way they alternate when attempting to connect. + * The goal behind it is first attempt to connect to IPv6, then to IPv4, then to IPv6 again until one of those + * attempts succeeds. + * + * @link https://tools.ietf.org/html/rfc8305#section-4 + * + * @internal + */ + public function mixIpsIntoConnectQueue(array $ips) + { + $this->ipsCount += \count($ips); + $connectQueueStash = $this->connectQueue; + $this->connectQueue = array(); + while (\count($connectQueueStash) > 0 || \count($ips) > 0) { + if (\count($ips) > 0) { + $this->connectQueue[] = \array_shift($ips); + } + if (\count($connectQueueStash) > 0) { + $this->connectQueue[] = \array_shift($connectQueueStash); + } + } + } +} \ No newline at end of file diff --git a/vendor/react/socket/src/HappyEyeBallsConnector.php b/vendor/react/socket/src/HappyEyeBallsConnector.php new file mode 100644 index 0000000..1d4a21d --- /dev/null +++ b/vendor/react/socket/src/HappyEyeBallsConnector.php @@ -0,0 +1,53 @@ +loop = $loop; + $this->connector = $connector; + $this->resolver = $resolver; + } + + public function connect($uri) + { + + if (\strpos($uri, '://') === false) { + $parts = \parse_url('tcp://' . $uri); + unset($parts['scheme']); + } else { + $parts = \parse_url($uri); + } + + if (!$parts || !isset($parts['host'])) { + return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid')); + } + + $host = \trim($parts['host'], '[]'); + + // skip DNS lookup / URI manipulation if this URI already contains an IP + if (false !== \filter_var($host, \FILTER_VALIDATE_IP)) { + return $this->connector->connect($uri); + } + + $builder = new HappyEyeBallsConnectionBuilder( + $this->loop, + $this->connector, + $this->resolver, + $uri, + $host, + $parts + ); + return $builder->connect(); + } +} diff --git a/vendor/react/socket/src/LimitingServer.php b/vendor/react/socket/src/LimitingServer.php index c7874ee..d19000b 100644 --- a/vendor/react/socket/src/LimitingServer.php +++ b/vendor/react/socket/src/LimitingServer.php @@ -21,8 +21,8 @@ * open connections. * * ```php - * $server = new LimitingServer($server, 100); - * $server->on('connection', function (ConnectionInterface $connection) { + * $server = new React\Socket\LimitingServer($server, 100); + * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { * $connection->write('hello there!' . PHP_EOL); * … * }); @@ -52,8 +52,8 @@ class LimitingServer extends EventEmitter implements ServerInterface * this and no `connection` event will be emitted. * * ```php - * $server = new LimitingServer($server, 100); - * $server->on('connection', function (ConnectionInterface $connection) { + * $server = new React\Socket\LimitingServer($server, 100); + * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { * $connection->write('hello there!' . PHP_EOL); * … * }); @@ -81,8 +81,8 @@ class LimitingServer extends EventEmitter implements ServerInterface * an interactive chat). * * ```php - * $server = new LimitingServer($server, 100, true); - * $server->on('connection', function (ConnectionInterface $connection) { + * $server = new React\Socket\LimitingServer($server, 100, true); + * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { * $connection->write('hello there!' . PHP_EOL); * … * }); @@ -156,8 +156,8 @@ public function close() public function handleConnection(ConnectionInterface $connection) { // close connection if limit exceeded - if ($this->limit !== null && count($this->connections) >= $this->limit) { - $this->handleError(new OverflowException('Connection closed because server reached connection limit')); + if ($this->limit !== null && \count($this->connections) >= $this->limit) { + $this->handleError(new \OverflowException('Connection closed because server reached connection limit')); $connection->close(); return; } @@ -169,7 +169,7 @@ public function handleConnection(ConnectionInterface $connection) }); // pause accepting new connections if limit exceeded - if ($this->pauseOnLimit && !$this->autoPaused && count($this->connections) >= $this->limit) { + if ($this->pauseOnLimit && !$this->autoPaused && \count($this->connections) >= $this->limit) { $this->autoPaused = true; if (!$this->manuPaused) { @@ -183,10 +183,10 @@ public function handleConnection(ConnectionInterface $connection) /** @internal */ public function handleDisconnection(ConnectionInterface $connection) { - unset($this->connections[array_search($connection, $this->connections)]); + unset($this->connections[\array_search($connection, $this->connections)]); // continue accepting new connection if below limit - if ($this->autoPaused && count($this->connections) < $this->limit) { + if ($this->autoPaused && \count($this->connections) < $this->limit) { $this->autoPaused = false; if (!$this->manuPaused) { @@ -196,7 +196,7 @@ public function handleDisconnection(ConnectionInterface $connection) } /** @internal */ - public function handleError(Exception $error) + public function handleError(\Exception $error) { $this->emit('error', array($error)); } diff --git a/vendor/react/socket/src/SecureConnector.php b/vendor/react/socket/src/SecureConnector.php index f04183d..e5ebc73 100644 --- a/vendor/react/socket/src/SecureConnector.php +++ b/vendor/react/socket/src/SecureConnector.php @@ -23,42 +23,62 @@ public function __construct(ConnectorInterface $connector, LoopInterface $loop, public function connect($uri) { - if (!function_exists('stream_socket_enable_crypto')) { - return Promise\reject(new BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)')); // @codeCoverageIgnore + if (!\function_exists('stream_socket_enable_crypto')) { + return Promise\reject(new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)')); // @codeCoverageIgnore } - if (strpos($uri, '://') === false) { + if (\strpos($uri, '://') === false) { $uri = 'tls://' . $uri; } - $parts = parse_url($uri); + $parts = \parse_url($uri); if (!$parts || !isset($parts['scheme']) || $parts['scheme'] !== 'tls') { - return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid')); + return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid')); } - $uri = str_replace('tls://', '', $uri); + $uri = \str_replace('tls://', '', $uri); $context = $this->context; $encryption = $this->streamEncryption; - return $this->connector->connect($uri)->then(function (ConnectionInterface $connection) use ($context, $encryption) { + $connected = false; + $promise = $this->connector->connect($uri)->then(function (ConnectionInterface $connection) use ($context, $encryption, $uri, &$promise, &$connected) { // (unencrypted) TCP/IP connection succeeded + $connected = true; if (!$connection instanceof Connection) { $connection->close(); - throw new UnexpectedValueException('Base connector does not use internal Connection class exposing stream resource'); + throw new \UnexpectedValueException('Base connector does not use internal Connection class exposing stream resource'); } // set required SSL/TLS context options foreach ($context as $name => $value) { - stream_context_set_option($connection->stream, 'ssl', $name, $value); + \stream_context_set_option($connection->stream, 'ssl', $name, $value); } // try to enable encryption - return $encryption->enable($connection)->then(null, function ($error) use ($connection) { + return $promise = $encryption->enable($connection)->then(null, function ($error) use ($connection, $uri) { // establishing encryption failed => close invalid connection and return error $connection->close(); - throw $error; + + throw new \RuntimeException( + 'Connection to ' . $uri . ' failed during TLS handshake: ' . $error->getMessage(), + $error->getCode() + ); }); }); + + return new \React\Promise\Promise( + function ($resolve, $reject) use ($promise) { + $promise->then($resolve, $reject); + }, + function ($_, $reject) use (&$promise, $uri, &$connected) { + if ($connected) { + $reject(new \RuntimeException('Connection to ' . $uri . ' cancelled during TLS handshake')); + } + + $promise->cancel(); + $promise = null; + } + ); } } diff --git a/vendor/react/socket/src/SecureServer.php b/vendor/react/socket/src/SecureServer.php index 302ae93..f091c34 100644 --- a/vendor/react/socket/src/SecureServer.php +++ b/vendor/react/socket/src/SecureServer.php @@ -15,8 +15,8 @@ * TCP/IP connections and then performs a TLS handshake for each connection. * * ```php - * $server = new TcpServer(8000, $loop); - * $server = new SecureServer($server, $loop, array( + * $server = new React\Socket\TcpServer(8000, $loop); + * $server = new React\Socket\SecureServer($server, $loop, array( * // tls context options here… * )); * ``` @@ -25,7 +25,7 @@ * with a connection instance implementing [`ConnectionInterface`](#connectioninterface): * * ```php - * $server->on('connection', function (ConnectionInterface $connection) { + * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { * echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL; * * $connection->write('hello there!' . PHP_EOL); @@ -67,8 +67,8 @@ final class SecureServer extends EventEmitter implements ServerInterface * PEM encoded certificate file: * * ```php - * $server = new TcpServer(8000, $loop); - * $server = new SecureServer($server, $loop, array( + * $server = new React\Socket\TcpServer(8000, $loop); + * $server = new React\Socket\SecureServer($server, $loop, array( * 'local_cert' => 'server.pem' * )); * ``` @@ -82,8 +82,8 @@ final class SecureServer extends EventEmitter implements ServerInterface * like this: * * ```php - * $server = new TcpServer(8000, $loop); - * $server = new SecureServer($server, $loop, array( + * $server = new React\Socket\TcpServer(8000, $loop); + * $server = new React\Socket\SecureServer($server, $loop, array( * 'local_cert' => 'server.pem', * 'passphrase' => 'secret' * )); @@ -113,12 +113,12 @@ final class SecureServer extends EventEmitter implements ServerInterface * @param array $context * @throws BadMethodCallException for legacy HHVM < 3.8 due to lack of support * @see TcpServer - * @link http://php.net/manual/en/context.ssl.php for TLS context options + * @link https://www.php.net/manual/en/context.ssl.php for TLS context options */ public function __construct(ServerInterface $tcp, LoopInterface $loop, array $context) { - if (!function_exists('stream_socket_enable_crypto')) { - throw new BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore + if (!\function_exists('stream_socket_enable_crypto')) { + throw new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore } // default to empty passphrase to suppress blocking passphrase prompt @@ -146,7 +146,7 @@ public function getAddress() return null; } - return str_replace('tcp://' , 'tls://', $address); + return \str_replace('tcp://' , 'tls://', $address); } public function pause() @@ -168,24 +168,31 @@ public function close() public function handleConnection(ConnectionInterface $connection) { if (!$connection instanceof Connection) { - $this->emit('error', array(new UnexpectedValueException('Base server does not use internal Connection class exposing stream resource'))); - $connection->end(); + $this->emit('error', array(new \UnexpectedValueException('Base server does not use internal Connection class exposing stream resource'))); + $connection->close(); return; } foreach ($this->context as $name => $value) { - stream_context_set_option($connection->stream, 'ssl', $name, $value); + \stream_context_set_option($connection->stream, 'ssl', $name, $value); } + // get remote address before starting TLS handshake in case connection closes during handshake + $remote = $connection->getRemoteAddress(); $that = $this; $this->encryption->enable($connection)->then( function ($conn) use ($that) { $that->emit('connection', array($conn)); }, - function ($error) use ($that, $connection) { + function ($error) use ($that, $connection, $remote) { + $error = new \RuntimeException( + 'Connection from ' . $remote . ' failed during TLS handshake: ' . $error->getMessage(), + $error->getCode() + ); + $that->emit('error', array($error)); - $connection->end(); + $connection->close(); } ); } diff --git a/vendor/react/socket/src/Server.php b/vendor/react/socket/src/Server.php index 72712e4..d62b668 100644 --- a/vendor/react/socket/src/Server.php +++ b/vendor/react/socket/src/Server.php @@ -25,9 +25,9 @@ public function __construct($uri, LoopInterface $loop, array $context = array()) ); $scheme = 'tcp'; - $pos = strpos($uri, '://'); + $pos = \strpos($uri, '://'); if ($pos !== false) { - $scheme = substr($uri, 0, $pos); + $scheme = \substr($uri, 0, $pos); } if ($scheme === 'unix') { diff --git a/vendor/react/socket/src/ServerInterface.php b/vendor/react/socket/src/ServerInterface.php index 5319678..beae751 100644 --- a/vendor/react/socket/src/ServerInterface.php +++ b/vendor/react/socket/src/ServerInterface.php @@ -23,7 +23,7 @@ * established, i.e. a new client connects to this server socket: * * ```php - * $server->on('connection', function (ConnectionInterface $connection) { + * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { * echo 'new connection' . PHP_EOL; * }); * ``` diff --git a/vendor/react/socket/src/StreamEncryption.php b/vendor/react/socket/src/StreamEncryption.php index ba5d472..0e0f3d6 100644 --- a/vendor/react/socket/src/StreamEncryption.php +++ b/vendor/react/socket/src/StreamEncryption.php @@ -19,44 +19,27 @@ class StreamEncryption private $method; private $server; - private $errstr; - private $errno; - public function __construct(LoopInterface $loop, $server = true) { $this->loop = $loop; $this->server = $server; // support TLSv1.0+ by default and exclude legacy SSLv2/SSLv3. - // PHP 5.6+ supports bitmasks, legacy PHP only supports predefined - // constants, so apply accordingly below. - // Also, since PHP 5.6.7 up until before PHP 7.2.0 the main constant did - // only support TLSv1.0, so we explicitly apply all versions. - // @link http://php.net/manual/en/migration56.openssl.php#migration56.openssl.crypto-method - // @link https://3v4l.org/plbFn + // As of PHP 7.2+ the main crypto method constant includes all TLS versions. + // As of PHP 5.6+ the crypto method is a bitmask, so we explicitly include all TLS versions. + // For legacy PHP < 5.6 the crypto method is a single value only and this constant includes all TLS versions. + // @link https://3v4l.org/9PSST if ($server) { - $this->method = STREAM_CRYPTO_METHOD_TLS_SERVER; + $this->method = \STREAM_CRYPTO_METHOD_TLS_SERVER; - if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER')) { - $this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_SERVER; - } - if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER')) { - $this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_SERVER; - } - if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER')) { - $this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_SERVER; + if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) { + $this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_1_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_2_SERVER; } } else { - $this->method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + $this->method = \STREAM_CRYPTO_METHOD_TLS_CLIENT; - if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT')) { - $this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT; - } - if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) { - $this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; - } - if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { - $this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) { + $this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; } } } @@ -80,7 +63,7 @@ public function toggle(Connection $stream, $toggle) $deferred = new Deferred(function ($_, $reject) use ($toggle) { // cancelling this leaves this stream in an inconsistent state… - $reject(new RuntimeException('Cancelled toggling encryption ' . $toggle ? 'on' : 'off')); + $reject(new \RuntimeException('Cancelled toggling encryption ' . $toggle ? 'on' : 'off')); }); // get actual stream socket from stream instance @@ -88,7 +71,7 @@ public function toggle(Connection $stream, $toggle) // get crypto method from context options or use global setting from constructor $method = $this->method; - $context = stream_context_get_options($socket); + $context = \stream_context_get_options($socket); if (isset($context['ssl']['crypto_method'])) { $method = $context['ssl']['crypto_method']; } @@ -122,25 +105,42 @@ public function toggle(Connection $stream, $toggle) public function toggleCrypto($socket, Deferred $deferred, $toggle, $method) { - set_error_handler(array($this, 'handleError')); - $result = stream_socket_enable_crypto($socket, $toggle, $method); - restore_error_handler(); + $error = null; + \set_error_handler(function ($_, $errstr) use (&$error) { + $error = \str_replace(array("\r", "\n"), ' ', $errstr); + + // remove useless function name from error message + if (($pos = \strpos($error, "): ")) !== false) { + $error = \substr($error, $pos + 3); + } + }); + + $result = \stream_socket_enable_crypto($socket, $toggle, $method); + + \restore_error_handler(); if (true === $result) { $deferred->resolve(); } else if (false === $result) { - $deferred->reject(new UnexpectedValueException( - sprintf("Unable to complete SSL/TLS handshake: %s", $this->errstr), - $this->errno - )); + // overwrite callback arguments for PHP7+ only, so they do not show + // up in the Exception trace and do not cause a possible cyclic reference. + $d = $deferred; + $deferred = null; + + if (\feof($socket) || $error === null) { + // EOF or failed without error => connection closed during handshake + $d->reject(new \UnexpectedValueException( + 'Connection lost during TLS handshake', + \defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 0 + )); + } else { + // handshake failed with error message + $d->reject(new \UnexpectedValueException( + 'Unable to complete TLS handshake: ' . $error + )); + } } else { // need more data, will retry } } - - public function handleError($errno, $errstr) - { - $this->errstr = str_replace(array("\r", "\n"), ' ', $errstr); - $this->errno = $errno; - } } diff --git a/vendor/react/socket/src/TcpConnector.php b/vendor/react/socket/src/TcpConnector.php index 53d55a3..961ae26 100644 --- a/vendor/react/socket/src/TcpConnector.php +++ b/vendor/react/socket/src/TcpConnector.php @@ -20,18 +20,18 @@ public function __construct(LoopInterface $loop, array $context = array()) public function connect($uri) { - if (strpos($uri, '://') === false) { + if (\strpos($uri, '://') === false) { $uri = 'tcp://' . $uri; } - $parts = parse_url($uri); + $parts = \parse_url($uri); if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { - return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid')); + return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid')); } - $ip = trim($parts['host'], '[]'); - if (false === filter_var($ip, FILTER_VALIDATE_IP)) { - return Promise\reject(new InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP')); + $ip = \trim($parts['host'], '[]'); + if (false === \filter_var($ip, \FILTER_VALIDATE_IP)) { + return Promise\reject(new \InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP')); } // use context given in constructor @@ -42,7 +42,7 @@ public function connect($uri) // parse arguments from query component of URI $args = array(); if (isset($parts['query'])) { - parse_str($parts['query'], $args); + \parse_str($parts['query'], $args); } // If an original hostname has been given, use this for TLS setup. @@ -59,7 +59,7 @@ public function connect($uri) // Legacy PHP < 5.6 ignores peer_name and requires legacy context options instead. // The SNI_server_name context option has to be set here during construction, // as legacy PHP ignores any values set later. - if (PHP_VERSION_ID < 50600) { + if (\PHP_VERSION_ID < 50600) { $context['ssl'] += array( 'SNI_server_name' => $args['hostname'], 'CN_match' => $args['hostname'] @@ -71,59 +71,50 @@ public function connect($uri) // HHVM fails to parse URIs with a query but no path, so let's simplify our URI here $remote = 'tcp://' . $parts['host'] . ':' . $parts['port']; - $socket = @stream_socket_client( + $stream = @\stream_socket_client( $remote, $errno, $errstr, 0, - STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT, - stream_context_create($context) + \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT, + \stream_context_create($context) ); - if (false === $socket) { - return Promise\reject(new RuntimeException( - sprintf("Connection to %s failed: %s", $uri, $errstr), + if (false === $stream) { + return Promise\reject(new \RuntimeException( + \sprintf("Connection to %s failed: %s", $uri, $errstr), $errno )); } - stream_set_blocking($socket, 0); - // wait for connection - - return $this->waitForStreamOnce($socket); - } - - private function waitForStreamOnce($stream) - { $loop = $this->loop; - - return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream) { - $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject) { + return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream, $uri) { + $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject, $uri) { $loop->removeWriteStream($stream); // The following hack looks like the only way to // detect connection refused errors with PHP's stream sockets. - if (false === stream_socket_get_name($stream, true)) { - fclose($stream); + if (false === \stream_socket_get_name($stream, true)) { + \fclose($stream); - $reject(new RuntimeException('Connection refused')); + $reject(new \RuntimeException('Connection to ' . $uri . ' failed: Connection refused')); } else { $resolve(new Connection($stream, $loop)); } }); - }, function () use ($loop, $stream) { + }, function () use ($loop, $stream, $uri) { $loop->removeWriteStream($stream); - fclose($stream); + \fclose($stream); // @codeCoverageIgnoreStart // legacy PHP 5.3 sometimes requires a second close call (see tests) - if (PHP_VERSION_ID < 50400 && is_resource($stream)) { - fclose($stream); + if (\PHP_VERSION_ID < 50400 && \is_resource($stream)) { + \fclose($stream); } // @codeCoverageIgnoreEnd - throw new RuntimeException('Cancelled while waiting for TCP/IP connection to be established'); + throw new \RuntimeException('Connection to ' . $uri . ' cancelled during TCP/IP handshake'); }); } } diff --git a/vendor/react/socket/src/TcpServer.php b/vendor/react/socket/src/TcpServer.php index 119e177..4b54a81 100644 --- a/vendor/react/socket/src/TcpServer.php +++ b/vendor/react/socket/src/TcpServer.php @@ -12,14 +12,14 @@ * is responsible for accepting plaintext TCP/IP connections. * * ```php - * $server = new TcpServer(8080, $loop); + * $server = new React\Socket\TcpServer(8080, $loop); * ``` * * Whenever a client connects, it will emit a `connection` event with a connection * instance implementing `ConnectionInterface`: * * ```php - * $server->on('connection', function (ConnectionInterface $connection) { + * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { * echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; * $connection->write('hello there!' . PHP_EOL); * … @@ -45,7 +45,7 @@ final class TcpServer extends EventEmitter implements ServerInterface * for more details. * * ```php - * $server = new TcpServer(8080, $loop); + * $server = new React\Socket\TcpServer(8080, $loop); * ``` * * As above, the `$uri` parameter can consist of only a port, in which case the @@ -55,7 +55,7 @@ final class TcpServer extends EventEmitter implements ServerInterface * In order to use a random port assignment, you can use the port `0`: * * ```php - * $server = new TcpServer(0, $loop); + * $server = new React\Socket\TcpServer(0, $loop); * $address = $server->getAddress(); * ``` * @@ -64,14 +64,14 @@ final class TcpServer extends EventEmitter implements ServerInterface * preceded by the `tcp://` scheme: * * ```php - * $server = new TcpServer('192.168.0.1:8080', $loop); + * $server = new React\Socket\TcpServer('192.168.0.1:8080', $loop); * ``` * * If you want to listen on an IPv6 address, you MUST enclose the host in square * brackets: * * ```php - * $server = new TcpServer('[::1]:8080', $loop); + * $server = new React\Socket\TcpServer('[::1]:8080', $loop); * ``` * * If the given URI is invalid, does not contain a port, any other scheme or if it @@ -79,7 +79,7 @@ final class TcpServer extends EventEmitter implements ServerInterface * * ```php * // throws InvalidArgumentException due to missing port - * $server = new TcpServer('127.0.0.1', $loop); + * $server = new React\Socket\TcpServer('127.0.0.1', $loop); * ``` * * If the given URI appears to be valid, but listening on it fails (such as if port @@ -87,10 +87,10 @@ final class TcpServer extends EventEmitter implements ServerInterface * throw a `RuntimeException`: * * ```php - * $first = new TcpServer(8080, $loop); + * $first = new React\Socket\TcpServer(8080, $loop); * * // throws RuntimeException because port is already in use - * $second = new TcpServer(8080, $loop); + * $second = new React\Socket\TcpServer(8080, $loop); * ``` * * Note that these error conditions may vary depending on your system and/or @@ -98,18 +98,18 @@ final class TcpServer extends EventEmitter implements ServerInterface * See the exception message and code for more details about the actual error * condition. * - * Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php) + * Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php) * for the underlying stream socket resource like this: * * ```php - * $server = new TcpServer('[::1]:8080', $loop, array( + * $server = new React\Socket\TcpServer('[::1]:8080', $loop, array( * 'backlog' => 200, * 'so_reuseport' => true, * 'ipv6_v6only' => true * )); * ``` * - * Note that available [socket context options](http://php.net/manual/en/context.socket.php), + * Note that available [socket context options](https://www.php.net/manual/en/context.socket.php), * their defaults and effects of changing these may vary depending on your system * and/or PHP version. * Passing unknown context options has no effect. @@ -130,57 +130,57 @@ public function __construct($uri, LoopInterface $loop, array $context = array()) } // assume default scheme if none has been given - if (strpos($uri, '://') === false) { + if (\strpos($uri, '://') === false) { $uri = 'tcp://' . $uri; } // parse_url() does not accept null ports (random port assignment) => manually remove - if (substr($uri, -2) === ':0') { - $parts = parse_url(substr($uri, 0, -2)); + if (\substr($uri, -2) === ':0') { + $parts = \parse_url(\substr($uri, 0, -2)); if ($parts) { $parts['port'] = 0; } } else { - $parts = parse_url($uri); + $parts = \parse_url($uri); } // ensure URI contains TCP scheme, host and port if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { - throw new InvalidArgumentException('Invalid URI "' . $uri . '" given'); + throw new \InvalidArgumentException('Invalid URI "' . $uri . '" given'); } - if (false === filter_var(trim($parts['host'], '[]'), FILTER_VALIDATE_IP)) { - throw new InvalidArgumentException('Given URI "' . $uri . '" does not contain a valid host IP'); + if (false === \filter_var(\trim($parts['host'], '[]'), \FILTER_VALIDATE_IP)) { + throw new \InvalidArgumentException('Given URI "' . $uri . '" does not contain a valid host IP'); } - $this->master = @stream_socket_server( + $this->master = @\stream_socket_server( $uri, $errno, $errstr, - STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, - stream_context_create(array('socket' => $context)) + \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN, + \stream_context_create(array('socket' => $context)) ); if (false === $this->master) { - throw new RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno); + throw new \RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno); } - stream_set_blocking($this->master, 0); + \stream_set_blocking($this->master, false); $this->resume(); } public function getAddress() { - if (!is_resource($this->master)) { + if (!\is_resource($this->master)) { return null; } - $address = stream_socket_get_name($this->master, false); + $address = \stream_socket_get_name($this->master, false); // check if this is an IPv6 address which includes multiple colons but no square brackets - $pos = strrpos($address, ':'); - if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') { - $port = substr($address, $pos + 1); - $address = '[' . substr($address, 0, $pos) . ']:' . $port; + $pos = \strrpos($address, ':'); + if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') { + $port = \substr($address, $pos + 1); + $address = '[' . \substr($address, 0, $pos) . ']:' . $port; } return 'tcp://' . $address; @@ -198,15 +198,15 @@ public function pause() public function resume() { - if ($this->listening || !is_resource($this->master)) { + if ($this->listening || !\is_resource($this->master)) { return; } $that = $this; $this->loop->addReadStream($this->master, function ($master) use ($that) { - $newSocket = @stream_socket_accept($master); + $newSocket = @\stream_socket_accept($master); if (false === $newSocket) { - $that->emit('error', array(new RuntimeException('Error accepting new connection'))); + $that->emit('error', array(new \RuntimeException('Error accepting new connection'))); return; } @@ -217,12 +217,12 @@ public function resume() public function close() { - if (!is_resource($this->master)) { + if (!\is_resource($this->master)) { return; } $this->pause(); - fclose($this->master); + \fclose($this->master); $this->removeAllListeners(); } diff --git a/vendor/react/socket/src/TimeoutConnector.php b/vendor/react/socket/src/TimeoutConnector.php index d4eba2e..33863e6 100644 --- a/vendor/react/socket/src/TimeoutConnector.php +++ b/vendor/react/socket/src/TimeoutConnector.php @@ -4,6 +4,7 @@ use React\EventLoop\LoopInterface; use React\Promise\Timer; +use React\Promise\Timer\TimeoutException; final class TimeoutConnector implements ConnectorInterface { @@ -20,6 +21,30 @@ public function __construct(ConnectorInterface $connector, $timeout, LoopInterfa public function connect($uri) { - return Timer\timeout($this->connector->connect($uri), $this->timeout, $this->loop); + return Timer\timeout($this->connector->connect($uri), $this->timeout, $this->loop)->then(null, self::handler($uri)); + } + + /** + * Creates a static rejection handler that reports a proper error message in case of a timeout. + * + * This uses a private static helper method to ensure this closure is not + * bound to this instance and the exception trace does not include a + * reference to this instance and its connector stack as a result. + * + * @param string $uri + * @return callable + */ + private static function handler($uri) + { + return function (\Exception $e) use ($uri) { + if ($e instanceof TimeoutException) { + throw new \RuntimeException( + 'Connection to ' . $uri . ' timed out after ' . $e->getTimeout() . ' seconds', + \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 0 + ); + } + + throw $e; + }; } } diff --git a/vendor/react/socket/src/UnixConnector.php b/vendor/react/socket/src/UnixConnector.php index 9b84ab0..881dad2 100644 --- a/vendor/react/socket/src/UnixConnector.php +++ b/vendor/react/socket/src/UnixConnector.php @@ -24,16 +24,16 @@ public function __construct(LoopInterface $loop) public function connect($path) { - if (strpos($path, '://') === false) { + if (\strpos($path, '://') === false) { $path = 'unix://' . $path; - } elseif (substr($path, 0, 7) !== 'unix://') { - return Promise\reject(new InvalidArgumentException('Given URI "' . $path . '" is invalid')); + } elseif (\substr($path, 0, 7) !== 'unix://') { + return Promise\reject(new \InvalidArgumentException('Given URI "' . $path . '" is invalid')); } - $resource = @stream_socket_client($path, $errno, $errstr, 1.0); + $resource = @\stream_socket_client($path, $errno, $errstr, 1.0); if (!$resource) { - return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno)); + return Promise\reject(new \RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno)); } $connection = new Connection($resource, $this->loop); diff --git a/vendor/react/socket/src/UnixServer.php b/vendor/react/socket/src/UnixServer.php index 8f1ed98..c174af4 100644 --- a/vendor/react/socket/src/UnixServer.php +++ b/vendor/react/socket/src/UnixServer.php @@ -12,7 +12,7 @@ * is responsible for accepting plaintext connections on unix domain sockets. * * ```php - * $server = new UnixServer('unix:///tmp/app.sock', $loop); + * $server = new React\Socket\UnixServer('unix:///tmp/app.sock', $loop); * ``` * * See also the `ServerInterface` for more details. @@ -34,7 +34,7 @@ final class UnixServer extends EventEmitter implements ServerInterface * for more details. * * ```php - * $server = new UnixServer('unix:///tmp/app.sock', $loop); + * $server = new React\Socket\UnixServer('unix:///tmp/app.sock', $loop); * ``` * * @param string $path @@ -47,34 +47,45 @@ public function __construct($path, LoopInterface $loop, array $context = array() { $this->loop = $loop; - if (strpos($path, '://') === false) { + if (\strpos($path, '://') === false) { $path = 'unix://' . $path; - } elseif (substr($path, 0, 7) !== 'unix://') { - throw new InvalidArgumentException('Given URI "' . $path . '" is invalid'); + } elseif (\substr($path, 0, 7) !== 'unix://') { + throw new \InvalidArgumentException('Given URI "' . $path . '" is invalid'); } - $this->master = @stream_socket_server( + $this->master = @\stream_socket_server( $path, $errno, $errstr, - STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, - stream_context_create(array('socket' => $context)) + \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN, + \stream_context_create(array('socket' => $context)) ); if (false === $this->master) { - throw new RuntimeException('Failed to listen on unix domain socket "' . $path . '": ' . $errstr, $errno); + // PHP does not seem to report errno/errstr for Unix domain sockets (UDS) right now. + // This only applies to UDS server sockets, see also https://3v4l.org/NAhpr. + // Parse PHP warning message containing unknown error, HHVM reports proper info at least. + if ($errno === 0 && $errstr === '') { + $error = \error_get_last(); + if (\preg_match('/\(([^\)]+)\)|\[(\d+)\]: (.*)/', $error['message'], $match)) { + $errstr = isset($match[3]) ? $match['3'] : $match[1]; + $errno = isset($match[2]) ? (int)$match[2] : 0; + } + } + + throw new \RuntimeException('Failed to listen on Unix domain socket "' . $path . '": ' . $errstr, $errno); } - stream_set_blocking($this->master, 0); + \stream_set_blocking($this->master, 0); $this->resume(); } public function getAddress() { - if (!is_resource($this->master)) { + if (!\is_resource($this->master)) { return null; } - return 'unix://' . stream_socket_get_name($this->master, false); + return 'unix://' . \stream_socket_get_name($this->master, false); } public function pause() @@ -95,9 +106,9 @@ public function resume() $that = $this; $this->loop->addReadStream($this->master, function ($master) use ($that) { - $newSocket = @stream_socket_accept($master); + $newSocket = @\stream_socket_accept($master); if (false === $newSocket) { - $that->emit('error', array(new RuntimeException('Error accepting new connection'))); + $that->emit('error', array(new \RuntimeException('Error accepting new connection'))); return; } @@ -108,12 +119,12 @@ public function resume() public function close() { - if (!is_resource($this->master)) { + if (!\is_resource($this->master)) { return; } $this->pause(); - fclose($this->master); + \fclose($this->master); $this->removeAllListeners(); } diff --git a/vendor/react/socket/tests/ConnectionTest.php b/vendor/react/socket/tests/ConnectionTest.php deleted file mode 100644 index d3563df..0000000 --- a/vendor/react/socket/tests/ConnectionTest.php +++ /dev/null @@ -1,47 +0,0 @@ -markTestSkipped('HHVM does not support socket operation on test memory stream'); - } - - $resource = fopen('php://memory', 'r+'); - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $connection = new Connection($resource, $loop); - $connection->close(); - - $this->assertFalse(is_resource($resource)); - } - - public function testCloseConnectionWillRemoveResourceFromLoopBeforeClosingResource() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM does not support socket operation on test memory stream'); - } - - $resource = fopen('php://memory', 'r+'); - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('addWriteStream')->with($resource); - - $onRemove = null; - $loop->expects($this->once())->method('removeWriteStream')->with($this->callback(function ($param) use (&$onRemove) { - $onRemove = is_resource($param); - return true; - })); - - $connection = new Connection($resource, $loop); - $connection->write('test'); - $connection->close(); - - $this->assertTrue($onRemove); - $this->assertFalse(is_resource($resource)); - } -} diff --git a/vendor/react/socket/tests/ConnectorTest.php b/vendor/react/socket/tests/ConnectorTest.php deleted file mode 100644 index c8eb19b..0000000 --- a/vendor/react/socket/tests/ConnectorTest.php +++ /dev/null @@ -1,128 +0,0 @@ -getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $promise = new Promise(function () { }); - $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $tcp->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise); - - $connector = new Connector($loop, array( - 'tcp' => $tcp - )); - - $connector->connect('127.0.0.1:80'); - } - - public function testConnectorPassedThroughHostnameIfDnsIsDisabled() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $promise = new Promise(function () { }); - $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $tcp->expects($this->once())->method('connect')->with('tcp://google.com:80')->willReturn($promise); - - $connector = new Connector($loop, array( - 'tcp' => $tcp, - 'dns' => false - )); - - $connector->connect('tcp://google.com:80'); - } - - public function testConnectorWithUnknownSchemeAlwaysFails() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new Connector($loop); - - $promise = $connector->connect('unknown://google.com:80'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testConnectorWithDisabledTcpDefaultSchemeAlwaysFails() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new Connector($loop, array( - 'tcp' => false - )); - - $promise = $connector->connect('google.com:80'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testConnectorWithDisabledTcpSchemeAlwaysFails() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new Connector($loop, array( - 'tcp' => false - )); - - $promise = $connector->connect('tcp://google.com:80'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testConnectorWithDisabledTlsSchemeAlwaysFails() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new Connector($loop, array( - 'tls' => false - )); - - $promise = $connector->connect('tls://google.com:443'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testConnectorWithDisabledUnixSchemeAlwaysFails() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new Connector($loop, array( - 'unix' => false - )); - - $promise = $connector->connect('unix://demo.sock'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testConnectorUsesGivenResolverInstance() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $promise = new Promise(function () { }); - $resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock(); - $resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise); - - $connector = new Connector($loop, array( - 'dns' => $resolver - )); - - $connector->connect('google.com:80'); - } - - public function testConnectorUsesResolvedHostnameIfDnsIsUsed() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $promise = new Promise(function ($resolve) { $resolve('127.0.0.1'); }); - $resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock(); - $resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise); - - $promise = new Promise(function () { }); - $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $tcp->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=google.com')->willReturn($promise); - - $connector = new Connector($loop, array( - 'tcp' => $tcp, - 'dns' => $resolver - )); - - $connector->connect('tcp://google.com:80'); - } -} diff --git a/vendor/react/socket/tests/DnsConnectorTest.php b/vendor/react/socket/tests/DnsConnectorTest.php deleted file mode 100644 index 3c94c39..0000000 --- a/vendor/react/socket/tests/DnsConnectorTest.php +++ /dev/null @@ -1,111 +0,0 @@ -tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $this->resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock(); - - $this->connector = new DnsConnector($this->tcp, $this->resolver); - } - - public function testPassByResolverIfGivenIp() - { - $this->resolver->expects($this->never())->method('resolve'); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('127.0.0.1:80'))->will($this->returnValue(Promise\reject())); - - $this->connector->connect('127.0.0.1:80'); - } - - public function testPassThroughResolverIfGivenHost() - { - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4'))); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=google.com'))->will($this->returnValue(Promise\reject())); - - $this->connector->connect('google.com:80'); - } - - public function testPassThroughResolverIfGivenHostWhichResolvesToIpv6() - { - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('::1'))); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('[::1]:80?hostname=google.com'))->will($this->returnValue(Promise\reject())); - - $this->connector->connect('google.com:80'); - } - - public function testPassByResolverIfGivenCompleteUri() - { - $this->resolver->expects($this->never())->method('resolve'); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://127.0.0.1:80/path?query#fragment'))->will($this->returnValue(Promise\reject())); - - $this->connector->connect('scheme://127.0.0.1:80/path?query#fragment'); - } - - public function testPassThroughResolverIfGivenCompleteUri() - { - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4'))); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://1.2.3.4:80/path?query&hostname=google.com#fragment'))->will($this->returnValue(Promise\reject())); - - $this->connector->connect('scheme://google.com:80/path?query#fragment'); - } - - public function testPassThroughResolverIfGivenExplicitHost() - { - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4'))); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://1.2.3.4:80/?hostname=google.de'))->will($this->returnValue(Promise\reject())); - - $this->connector->connect('scheme://google.com:80/?hostname=google.de'); - } - - public function testRejectsImmediatelyIfUriIsInvalid() - { - $this->resolver->expects($this->never())->method('resolve'); - $this->tcp->expects($this->never())->method('connect'); - - $promise = $this->connector->connect('////'); - - $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); - } - - public function testSkipConnectionIfDnsFails() - { - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.invalid'))->will($this->returnValue(Promise\reject())); - $this->tcp->expects($this->never())->method('connect'); - - $this->connector->connect('example.invalid:80'); - } - - public function testCancelDuringDnsCancelsDnsAndDoesNotStartTcpConnection() - { - $pending = new Promise\Promise(function () { }, $this->expectCallableOnce()); - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->will($this->returnValue($pending)); - $this->tcp->expects($this->never())->method('connect'); - - $promise = $this->connector->connect('example.com:80'); - $promise->cancel(); - - $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); - } - - public function testCancelDuringTcpConnectionCancelsTcpConnection() - { - $pending = new Promise\Promise(function () { }, function () { throw new \Exception(); }); - $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->will($this->returnValue(Promise\resolve('1.2.3.4'))); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=example.com'))->will($this->returnValue($pending)); - - $promise = $this->connector->connect('example.com:80'); - $promise->cancel(); - - $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); - } -} diff --git a/vendor/react/socket/tests/FixedUriConnectorTest.php b/vendor/react/socket/tests/FixedUriConnectorTest.php deleted file mode 100644 index f42d74f..0000000 --- a/vendor/react/socket/tests/FixedUriConnectorTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $base->expects($this->once())->method('connect')->with('test')->willReturn('ret'); - - $connector = new FixedUriConnector('test', $base); - - $this->assertEquals('ret', $connector->connect('ignored')); - } -} diff --git a/vendor/react/socket/tests/FunctionalConnectorTest.php b/vendor/react/socket/tests/FunctionalConnectorTest.php deleted file mode 100644 index 6611352..0000000 --- a/vendor/react/socket/tests/FunctionalConnectorTest.php +++ /dev/null @@ -1,32 +0,0 @@ -on('connection', $this->expectCallableOnce()); - $server->on('connection', array($server, 'close')); - - $connector = new Connector($loop); - - $connection = Block\await($connector->connect('localhost:9998'), $loop, self::TIMEOUT); - - $this->assertInstanceOf('React\Socket\ConnectionInterface', $connection); - - $connection->close(); - $server->close(); - } -} diff --git a/vendor/react/socket/tests/FunctionalSecureServerTest.php b/vendor/react/socket/tests/FunctionalSecureServerTest.php deleted file mode 100644 index 78a59d0..0000000 --- a/vendor/react/socket/tests/FunctionalSecureServerTest.php +++ /dev/null @@ -1,438 +0,0 @@ -markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - } - - public function testEmitsConnectionForNewConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - Block\await($promise, $loop, self::TIMEOUT); - } - - public function testWritesDataToConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $server->on('connection', function (ConnectionInterface $conn) { - $conn->write('foo'); - }); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $local = Block\await($promise, $loop, self::TIMEOUT); - /* @var $local ConnectionInterface */ - - $local->on('data', $this->expectCallableOnceWith('foo')); - - Block\sleep(self::TIMEOUT, $loop); - } - - public function testWritesDataInMultipleChunksToConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $server->on('connection', function (ConnectionInterface $conn) { - $conn->write(str_repeat('*', 400000)); - }); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $local = Block\await($promise, $loop, self::TIMEOUT); - /* @var $local React\Stream\Stream */ - - $received = 0; - $local->on('data', function ($chunk) use (&$received) { - $received += strlen($chunk); - }); - - Block\sleep(self::TIMEOUT, $loop); - - $this->assertEquals(400000, $received); - } - - public function testWritesMoreDataInMultipleChunksToConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $server->on('connection', function (ConnectionInterface $conn) { - $conn->write(str_repeat('*', 2000000)); - }); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $local = Block\await($promise, $loop, self::TIMEOUT); - /* @var $local React\Stream\Stream */ - - $received = 0; - $local->on('data', function ($chunk) use (&$received) { - $received += strlen($chunk); - }); - - Block\sleep(self::TIMEOUT, $loop); - - $this->assertEquals(2000000, $received); - } - - public function testEmitsDataFromConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $once = $this->expectCallableOnceWith('foo'); - $server->on('connection', function (ConnectionInterface $conn) use ($once) { - $conn->on('data', $once); - }); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $local = Block\await($promise, $loop, self::TIMEOUT); - /* @var $local React\Stream\Stream */ - - $local->write("foo"); - - Block\sleep(self::TIMEOUT, $loop); - } - - public function testEmitsDataInMultipleChunksFromConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $received = 0; - $server->on('connection', function (ConnectionInterface $conn) use (&$received) { - $conn->on('data', function ($chunk) use (&$received) { - $received += strlen($chunk); - }); - }); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $local = Block\await($promise, $loop, self::TIMEOUT); - /* @var $local React\Stream\Stream */ - - $local->write(str_repeat('*', 400000)); - - Block\sleep(self::TIMEOUT, $loop); - - $this->assertEquals(400000, $received); - } - - public function testPipesDataBackInMultipleChunksFromConnection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableOnce()); - - $server->on('connection', function (ConnectionInterface $conn) use (&$received) { - $conn->pipe($conn); - }); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $local = Block\await($promise, $loop, self::TIMEOUT); - /* @var $local React\Stream\Stream */ - - $received = 0; - $local->on('data', function ($chunk) use (&$received) { - $received += strlen($chunk); - }); - - $local->write(str_repeat('*', 400000)); - - Block\sleep(self::TIMEOUT, $loop); - - $this->assertEquals(400000, $received); - } - - /** - * @requires PHP 5.6 - */ - public function testEmitsConnectionForNewTlsv11Connection() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem', - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_SERVER - )); - $server->on('connection', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false, - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT - )); - $promise = $connector->connect($server->getAddress()); - - Block\await($promise, $loop, self::TIMEOUT); - } - - /** - * @requires PHP 5.6 - */ - public function testEmitsErrorForClientWithTlsVersionMismatch() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem', - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_SERVER|STREAM_CRYPTO_METHOD_TLSv1_2_SERVER - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false, - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT - )); - $promise = $connector->connect($server->getAddress()); - - $this->setExpectedException('RuntimeException', 'handshake'); - Block\await($promise, $loop, self::TIMEOUT); - } - - public function testEmitsConnectionForNewConnectionWithEncryptedCertificate() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem', - 'passphrase' => 'swordfish' - )); - $server->on('connection', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - Block\await($promise, $loop, self::TIMEOUT); - } - - public function testEmitsErrorForServerWithInvalidCertificate() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => 'invalid.pem' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $this->setExpectedException('RuntimeException', 'handshake'); - Block\await($promise, $loop, self::TIMEOUT); - } - - public function testEmitsErrorForServerWithEncryptedCertificateMissingPassphrase() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $this->setExpectedException('RuntimeException', 'handshake'); - Block\await($promise, $loop, self::TIMEOUT); - } - - public function testEmitsErrorForServerWithEncryptedCertificateWithInvalidPassphrase() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem', - 'passphrase' => 'nope' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - - $this->setExpectedException('RuntimeException', 'handshake'); - Block\await($promise, $loop, self::TIMEOUT); - } - - public function testEmitsErrorForConnectionWithPeerVerification() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => true - )); - $promise = $connector->connect($server->getAddress()); - - $promise->then(null, $this->expectCallableOnce()); - Block\sleep(self::TIMEOUT, $loop); - } - - public function testEmitsErrorIfConnectionIsCancelled() - { - if (PHP_OS !== 'Linux') { - $this->markTestSkipped('Linux only (OS is ' . PHP_OS . ')'); - } - - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new SecureConnector(new TcpConnector($loop), $loop, array( - 'verify_peer' => false - )); - $promise = $connector->connect($server->getAddress()); - $promise->cancel(); - - $promise->then(null, $this->expectCallableOnce()); - Block\sleep(self::TIMEOUT, $loop); - } - - public function testEmitsNothingIfConnectionIsIdle() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableNever()); - - $connector = new TcpConnector($loop); - $promise = $connector->connect(str_replace('tls://', '', $server->getAddress())); - - $promise->then($this->expectCallableOnce()); - Block\sleep(self::TIMEOUT, $loop); - } - - public function testEmitsErrorIfConnectionIsNotSecureHandshake() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new SecureServer($server, $loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $server->on('connection', $this->expectCallableNever()); - $server->on('error', $this->expectCallableOnce()); - - $connector = new TcpConnector($loop); - $promise = $connector->connect(str_replace('tls://', '', $server->getAddress())); - - $promise->then(function (ConnectionInterface $stream) { - $stream->write("GET / HTTP/1.0\r\n\r\n"); - }); - - Block\sleep(self::TIMEOUT, $loop); - } -} diff --git a/vendor/react/socket/tests/FunctionalTcpServerTest.php b/vendor/react/socket/tests/FunctionalTcpServerTest.php deleted file mode 100644 index ec7855e..0000000 --- a/vendor/react/socket/tests/FunctionalTcpServerTest.php +++ /dev/null @@ -1,324 +0,0 @@ -on('connection', $this->expectCallableOnce()); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - } - - public function testEmitsNoConnectionForNewConnectionWhenPaused() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server->on('connection', $this->expectCallableNever()); - $server->pause(); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - } - - public function testEmitsConnectionForNewConnectionWhenResumedAfterPause() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server->on('connection', $this->expectCallableOnce()); - $server->pause(); - $server->resume(); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - } - - public function testEmitsConnectionWithRemoteIp() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $peer = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { - $peer = $conn->getRemoteAddress(); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertContains('127.0.0.1:', $peer); - } - - public function testEmitsConnectionWithLocalIp() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $local = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$local) { - $local = $conn->getLocalAddress(); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertContains('127.0.0.1:', $local); - $this->assertEquals($server->getAddress(), $local); - } - - public function testEmitsConnectionWithLocalIpDespiteListeningOnAll() - { - $loop = Factory::create(); - - $server = new TcpServer('0.0.0.0:0', $loop); - $local = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$local) { - $local = $conn->getLocalAddress(); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertContains('127.0.0.1:', $local); - } - - public function testEmitsConnectionWithRemoteIpAfterConnectionIsClosedByPeer() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $peer = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { - $conn->on('close', function () use ($conn, &$peer) { - $peer = $conn->getRemoteAddress(); - }); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $client = Block\await($promise, $loop, 0.1); - $client->end(); - - Block\sleep(0.1, $loop); - - $this->assertContains('127.0.0.1:', $peer); - } - - public function testEmitsConnectionWithRemoteNullAddressAfterConnectionIsClosedLocally() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $peer = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { - $conn->close(); - $peer = $conn->getRemoteAddress(); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertNull($peer); - } - - public function testEmitsConnectionEvenIfConnectionIsCancelled() - { - if (PHP_OS !== 'Linux') { - $this->markTestSkipped('Linux only (OS is ' . PHP_OS . ')'); - } - - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server->on('connection', $this->expectCallableOnce()); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - $promise->cancel(); - - $promise->then(null, $this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - } - - public function testEmitsConnectionForNewIpv6Connection() - { - $loop = Factory::create(); - - try { - $server = new TcpServer('[::1]:0', $loop); - } catch (\RuntimeException $e) { - $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)'); - } - - $server->on('connection', $this->expectCallableOnce()); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - } - - public function testEmitsConnectionWithRemoteIpv6() - { - $loop = Factory::create(); - - try { - $server = new TcpServer('[::1]:0', $loop); - } catch (\RuntimeException $e) { - $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)'); - } - - $peer = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { - $peer = $conn->getRemoteAddress(); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertContains('[::1]:', $peer); - } - - public function testEmitsConnectionWithLocalIpv6() - { - $loop = Factory::create(); - - try { - $server = new TcpServer('[::1]:0', $loop); - } catch (\RuntimeException $e) { - $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)'); - } - - $local = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$local) { - $local = $conn->getLocalAddress(); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertContains('[::1]:', $local); - $this->assertEquals($server->getAddress(), $local); - } - - public function testEmitsConnectionWithInheritedContextOptions() - { - if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) { - // https://3v4l.org/hB4Tc - $this->markTestSkipped('Not supported on legacy HHVM < 3.13'); - } - - $loop = Factory::create(); - - $server = new TcpServer(0, $loop, array( - 'backlog' => 4 - )); - - $all = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$all) { - $all = stream_context_get_options($conn->stream); - }); - - $connector = new TcpConnector($loop); - $promise = $connector->connect($server->getAddress()); - - $promise->then($this->expectCallableOnce()); - - Block\sleep(0.1, $loop); - - $this->assertEquals(array('socket' => array('backlog' => 4)), $all); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFailsToListenOnInvalidUri() - { - $loop = Factory::create(); - - new TcpServer('///', $loop); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFailsToListenOnUriWithoutPort() - { - $loop = Factory::create(); - - new TcpServer('127.0.0.1', $loop); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFailsToListenOnUriWithWrongScheme() - { - $loop = Factory::create(); - - new TcpServer('udp://127.0.0.1:0', $loop); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFailsToListenOnUriWIthHostname() - { - $loop = Factory::create(); - - new TcpServer('localhost:8080', $loop); - } -} diff --git a/vendor/react/socket/tests/IntegrationTest.php b/vendor/react/socket/tests/IntegrationTest.php deleted file mode 100644 index 59dff4f..0000000 --- a/vendor/react/socket/tests/IntegrationTest.php +++ /dev/null @@ -1,328 +0,0 @@ -connect('google.com:80'), $loop); - - $this->assertContains(':80', $conn->getRemoteAddress()); - $this->assertNotEquals('google.com:80', $conn->getRemoteAddress()); - - $conn->write("GET / HTTP/1.0\r\n\r\n"); - - $response = $this->buffer($conn, $loop, self::TIMEOUT); - - $this->assertRegExp('#^HTTP/1\.0#', $response); - } - - /** @test */ - public function gettingEncryptedStuffFromGoogleShouldWork() - { - if (!function_exists('stream_socket_enable_crypto')) { - $this->markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $loop = Factory::create(); - $secureConnector = new Connector($loop); - - $conn = Block\await($secureConnector->connect('tls://google.com:443'), $loop); - - $conn->write("GET / HTTP/1.0\r\n\r\n"); - - $response = $this->buffer($conn, $loop, self::TIMEOUT); - - $this->assertRegExp('#^HTTP/1\.0#', $response); - } - - /** @test */ - public function gettingEncryptedStuffFromGoogleShouldWorkIfHostIsResolvedFirst() - { - if (!function_exists('stream_socket_enable_crypto')) { - $this->markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $loop = Factory::create(); - - $factory = new ResolverFactory(); - $dns = $factory->create('8.8.8.8', $loop); - - $connector = new DnsConnector( - new SecureConnector( - new TcpConnector($loop), - $loop - ), - $dns - ); - - $conn = Block\await($connector->connect('google.com:443'), $loop); - - $conn->write("GET / HTTP/1.0\r\n\r\n"); - - $response = $this->buffer($conn, $loop, self::TIMEOUT); - - $this->assertRegExp('#^HTTP/1\.0#', $response); - } - - /** @test */ - public function gettingPlaintextStuffFromEncryptedGoogleShouldNotWork() - { - $loop = Factory::create(); - $connector = new Connector($loop); - - $conn = Block\await($connector->connect('google.com:443'), $loop); - - $this->assertContains(':443', $conn->getRemoteAddress()); - $this->assertNotEquals('google.com:443', $conn->getRemoteAddress()); - - $conn->write("GET / HTTP/1.0\r\n\r\n"); - - $response = $this->buffer($conn, $loop, self::TIMEOUT); - - $this->assertNotRegExp('#^HTTP/1\.0#', $response); - } - - public function testConnectingFailsIfDnsUsesInvalidResolver() - { - $loop = Factory::create(); - - $factory = new ResolverFactory(); - $dns = $factory->create('demo.invalid', $loop); - - $connector = new Connector($loop, array( - 'dns' => $dns - )); - - $this->setExpectedException('RuntimeException'); - Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT); - } - - public function testCancellingPendingConnectionWithoutTimeoutShouldNotCreateAnyGarbageReferences() - { - if (class_exists('React\Promise\When')) { - $this->markTestSkipped('Not supported on legacy Promise v1 API'); - } - - $loop = Factory::create(); - $connector = new Connector($loop, array('timeout' => false)); - - gc_collect_cycles(); - $promise = $connector->connect('8.8.8.8:80'); - $promise->cancel(); - unset($promise); - - $this->assertEquals(0, gc_collect_cycles()); - } - - public function testCancellingPendingConnectionShouldNotCreateAnyGarbageReferences() - { - if (class_exists('React\Promise\When')) { - $this->markTestSkipped('Not supported on legacy Promise v1 API'); - } - - $loop = Factory::create(); - $connector = new Connector($loop); - - gc_collect_cycles(); - $promise = $connector->connect('8.8.8.8:80'); - $promise->cancel(); - unset($promise); - - $this->assertEquals(0, gc_collect_cycles()); - } - - public function testWaitingForRejectedConnectionShouldNotCreateAnyGarbageReferences() - { - if (class_exists('React\Promise\When')) { - $this->markTestSkipped('Not supported on legacy Promise v1 API'); - } - - $loop = Factory::create(); - $connector = new Connector($loop, array('timeout' => false)); - - gc_collect_cycles(); - - $wait = true; - $promise = $connector->connect('127.0.0.1:1')->then( - null, - function ($e) use (&$wait) { - $wait = false; - throw $e; - } - ); - - // run loop for short period to ensure we detect connection refused error - Block\sleep(0.01, $loop); - if ($wait) { - Block\sleep(0.2, $loop); - if ($wait) { - $this->fail('Connection attempt did not fail'); - } - } - unset($promise); - - $this->assertEquals(0, gc_collect_cycles()); - } - - /** - * @requires PHP 7 - */ - public function testWaitingForConnectionTimeoutShouldNotCreateAnyGarbageReferences() - { - if (class_exists('React\Promise\When')) { - $this->markTestSkipped('Not supported on legacy Promise v1 API'); - } - - $loop = Factory::create(); - $connector = new Connector($loop, array('timeout' => 0.001)); - - gc_collect_cycles(); - - $wait = true; - $promise = $connector->connect('google.com:80')->then( - null, - function ($e) use (&$wait) { - $wait = false; - throw $e; - } - ); - - // run loop for short period to ensure we detect connection timeout error - Block\sleep(0.01, $loop); - if ($wait) { - Block\sleep(0.2, $loop); - if ($wait) { - $this->fail('Connection attempt did not fail'); - } - } - unset($promise); - - $this->assertEquals(0, gc_collect_cycles()); - } - - public function testWaitingForInvalidDnsConnectionShouldNotCreateAnyGarbageReferences() - { - if (class_exists('React\Promise\When')) { - $this->markTestSkipped('Not supported on legacy Promise v1 API'); - } - - $loop = Factory::create(); - $connector = new Connector($loop, array('timeout' => false)); - - gc_collect_cycles(); - - $wait = true; - $promise = $connector->connect('example.invalid:80')->then( - null, - function ($e) use (&$wait) { - $wait = false; - throw $e; - } - ); - - // run loop for short period to ensure we detect DNS error - Block\sleep(0.01, $loop); - if ($wait) { - Block\sleep(0.2, $loop); - if ($wait) { - $this->fail('Connection attempt did not fail'); - } - } - unset($promise); - - $this->assertEquals(0, gc_collect_cycles()); - } - - public function testWaitingForSuccessfullyClosedConnectionShouldNotCreateAnyGarbageReferences() - { - if (class_exists('React\Promise\When')) { - $this->markTestSkipped('Not supported on legacy Promise v1 API'); - } - - $loop = Factory::create(); - $connector = new Connector($loop, array('timeout' => false)); - - gc_collect_cycles(); - $promise = $connector->connect('google.com:80')->then( - function ($conn) { - $conn->close(); - } - ); - Block\await($promise, $loop, self::TIMEOUT); - unset($promise); - - $this->assertEquals(0, gc_collect_cycles()); - } - - public function testConnectingFailsIfTimeoutIsTooSmall() - { - if (!function_exists('stream_socket_enable_crypto')) { - $this->markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $loop = Factory::create(); - - $connector = new Connector($loop, array( - 'timeout' => 0.001 - )); - - $this->setExpectedException('RuntimeException'); - Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT); - } - - public function testSelfSignedRejectsIfVerificationIsEnabled() - { - if (!function_exists('stream_socket_enable_crypto')) { - $this->markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $loop = Factory::create(); - - $connector = new Connector($loop, array( - 'tls' => array( - 'verify_peer' => true - ) - )); - - $this->setExpectedException('RuntimeException'); - Block\await($connector->connect('tls://self-signed.badssl.com:443'), $loop, self::TIMEOUT); - } - - public function testSelfSignedResolvesIfVerificationIsDisabled() - { - if (!function_exists('stream_socket_enable_crypto')) { - $this->markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $loop = Factory::create(); - - $connector = new Connector($loop, array( - 'tls' => array( - 'verify_peer' => false - ) - )); - - $conn = Block\await($connector->connect('tls://self-signed.badssl.com:443'), $loop, self::TIMEOUT); - $conn->close(); - - // if we reach this, then everything is good - $this->assertNull(null); - } -} diff --git a/vendor/react/socket/tests/LimitingServerTest.php b/vendor/react/socket/tests/LimitingServerTest.php deleted file mode 100644 index 2cc9a58..0000000 --- a/vendor/react/socket/tests/LimitingServerTest.php +++ /dev/null @@ -1,195 +0,0 @@ -getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('getAddress')->willReturn('127.0.0.1:1234'); - - $server = new LimitingServer($tcp, 100); - - $this->assertEquals('127.0.0.1:1234', $server->getAddress()); - } - - public function testPauseWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('pause'); - - $server = new LimitingServer($tcp, 100); - - $server->pause(); - } - - public function testPauseTwiceWillBePassedThroughToTcpServerOnce() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('pause'); - - $server = new LimitingServer($tcp, 100); - - $server->pause(); - $server->pause(); - } - - public function testResumeWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('resume'); - - $server = new LimitingServer($tcp, 100); - - $server->pause(); - $server->resume(); - } - - public function testResumeTwiceWillBePassedThroughToTcpServerOnce() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('resume'); - - $server = new LimitingServer($tcp, 100); - - $server->pause(); - $server->resume(); - $server->resume(); - } - - public function testCloseWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('close'); - - $server = new LimitingServer($tcp, 100); - - $server->close(); - } - - public function testSocketErrorWillBeForwarded() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $tcp = new TcpServer(0, $loop); - - $server = new LimitingServer($tcp, 100); - - $server->on('error', $this->expectCallableOnce()); - - $tcp->emit('error', array(new \RuntimeException('test'))); - } - - public function testSocketConnectionWillBeForwarded() - { - $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $tcp = new TcpServer(0, $loop); - - $server = new LimitingServer($tcp, 100); - $server->on('connection', $this->expectCallableOnceWith($connection)); - $server->on('error', $this->expectCallableNever()); - - $tcp->emit('connection', array($connection)); - - $this->assertEquals(array($connection), $server->getConnections()); - } - - public function testSocketConnectionWillBeClosedOnceLimitIsReached() - { - $first = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); - $first->expects($this->never())->method('close'); - $second = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); - $second->expects($this->once())->method('close'); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $tcp = new TcpServer(0, $loop); - - $server = new LimitingServer($tcp, 1); - $server->on('connection', $this->expectCallableOnceWith($first)); - $server->on('error', $this->expectCallableOnce()); - - $tcp->emit('connection', array($first)); - $tcp->emit('connection', array($second)); - } - - public function testPausingServerWillBePausedOnceLimitIsReached() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('addReadStream'); - $loop->expects($this->once())->method('removeReadStream'); - - $tcp = new TcpServer(0, $loop); - - $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); - - $server = new LimitingServer($tcp, 1, true); - - $tcp->emit('connection', array($connection)); - } - - public function testSocketDisconnectionWillRemoveFromList() - { - $loop = Factory::create(); - - $tcp = new TcpServer(0, $loop); - - $socket = stream_socket_client($tcp->getAddress()); - fclose($socket); - - $server = new LimitingServer($tcp, 100); - $server->on('connection', $this->expectCallableOnce()); - $server->on('error', $this->expectCallableNever()); - - Block\sleep(0.1, $loop); - - $this->assertEquals(array(), $server->getConnections()); - } - - public function testPausingServerWillEmitOnlyOneButAcceptTwoConnectionsDueToOperatingSystem() - { - $loop = Factory::create(); - - $server = new TcpServer(0, $loop); - $server = new LimitingServer($server, 1, true); - $server->on('connection', $this->expectCallableOnce()); - $server->on('error', $this->expectCallableNever()); - - $first = stream_socket_client($server->getAddress()); - $second = stream_socket_client($server->getAddress()); - - Block\sleep(0.1, $loop); - - fclose($first); - fclose($second); - } - - public function testPausingServerWillEmitTwoConnectionsFromBacklog() - { - $loop = Factory::create(); - - $twice = $this->createCallableMock(); - $twice->expects($this->exactly(2))->method('__invoke'); - - $server = new TcpServer(0, $loop); - $server = new LimitingServer($server, 1, true); - $server->on('connection', $twice); - $server->on('error', $this->expectCallableNever()); - - $first = stream_socket_client($server->getAddress()); - fclose($first); - $second = stream_socket_client($server->getAddress()); - fclose($second); - - Block\sleep(0.1, $loop); - } -} diff --git a/vendor/react/socket/tests/SecureConnectorTest.php b/vendor/react/socket/tests/SecureConnectorTest.php deleted file mode 100644 index 0b3a702..0000000 --- a/vendor/react/socket/tests/SecureConnectorTest.php +++ /dev/null @@ -1,74 +0,0 @@ -markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $this->tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $this->connector = new SecureConnector($this->tcp, $this->loop); - } - - public function testConnectionWillWaitForTcpConnection() - { - $pending = new Promise\Promise(function () { }); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending)); - - $promise = $this->connector->connect('example.com:80'); - - $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); - } - - public function testConnectionWithCompleteUriWillBePassedThroughExpectForScheme() - { - $pending = new Promise\Promise(function () { }); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80/path?query#fragment'))->will($this->returnValue($pending)); - - $this->connector->connect('tls://example.com:80/path?query#fragment'); - } - - public function testConnectionToInvalidSchemeWillReject() - { - $this->tcp->expects($this->never())->method('connect'); - - $promise = $this->connector->connect('tcp://example.com:80'); - - $promise->then(null, $this->expectCallableOnce()); - } - - public function testCancelDuringTcpConnectionCancelsTcpConnection() - { - $pending = new Promise\Promise(function () { }, function () { throw new \Exception(); }); - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending)); - - $promise = $this->connector->connect('example.com:80'); - $promise->cancel(); - - $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); - } - - public function testConnectionWillBeClosedAndRejectedIfConnectioIsNoStream() - { - $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); - $connection->expects($this->once())->method('close'); - - $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->willReturn(Promise\resolve($connection)); - - $promise = $this->connector->connect('example.com:80'); - - $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); - } -} diff --git a/vendor/react/socket/tests/SecureIntegrationTest.php b/vendor/react/socket/tests/SecureIntegrationTest.php deleted file mode 100644 index 8c9ba14..0000000 --- a/vendor/react/socket/tests/SecureIntegrationTest.php +++ /dev/null @@ -1,204 +0,0 @@ -markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $this->loop = LoopFactory::create(); - $this->server = new TcpServer(0, $this->loop); - $this->server = new SecureServer($this->server, $this->loop, array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - )); - $this->address = $this->server->getAddress(); - $this->connector = new SecureConnector(new TcpConnector($this->loop), $this->loop, array('verify_peer' => false)); - } - - public function tearDown() - { - if ($this->server !== null) { - $this->server->close(); - $this->server = null; - } - } - - public function testConnectToServer() - { - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - $client->close(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testConnectToServerEmitsConnection() - { - $promiseServer = $this->createPromiseForEvent($this->server, 'connection', $this->expectCallableOnce()); - - $promiseClient = $this->connector->connect($this->address); - - list($_, $client) = Block\awaitAll(array($promiseServer, $promiseClient), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - $client->close(); - } - - public function testSendSmallDataToServerReceivesOneChunk() - { - // server expects one connection which emits one data event - $received = new Deferred(); - $this->server->on('connection', function (ConnectionInterface $peer) use ($received) { - $peer->on('data', function ($chunk) use ($received) { - $received->resolve($chunk); - }); - }); - - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - $client->write('hello'); - - // await server to report one "data" event - $data = Block\await($received->promise(), $this->loop, self::TIMEOUT); - - $client->close(); - - $this->assertEquals('hello', $data); - } - - public function testSendDataWithEndToServerReceivesAllData() - { - $disconnected = new Deferred(); - $this->server->on('connection', function (ConnectionInterface $peer) use ($disconnected) { - $received = ''; - $peer->on('data', function ($chunk) use (&$received) { - $received .= $chunk; - }); - $peer->on('close', function () use (&$received, $disconnected) { - $disconnected->resolve($received); - }); - }); - - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - $data = str_repeat('a', 200000); - $client->end($data); - - // await server to report connection "close" event - $received = Block\await($disconnected->promise(), $this->loop, self::TIMEOUT); - - $this->assertEquals($data, $received); - } - - public function testSendDataWithoutEndingToServerReceivesAllData() - { - $received = ''; - $this->server->on('connection', function (ConnectionInterface $peer) use (&$received) { - $peer->on('data', function ($chunk) use (&$received) { - $received .= $chunk; - }); - }); - - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - $data = str_repeat('d', 200000); - $client->write($data); - - // buffer incoming data for 0.1s (should be plenty of time) - Block\sleep(0.1, $this->loop); - - $client->close(); - - $this->assertEquals($data, $received); - } - - public function testConnectToServerWhichSendsSmallDataReceivesOneChunk() - { - $this->server->on('connection', function (ConnectionInterface $peer) { - $peer->write('hello'); - }); - - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - // await client to report one "data" event - $receive = $this->createPromiseForEvent($client, 'data', $this->expectCallableOnceWith('hello')); - Block\await($receive, $this->loop, self::TIMEOUT); - - $client->close(); - } - - public function testConnectToServerWhichSendsDataWithEndReceivesAllData() - { - $data = str_repeat('b', 100000); - $this->server->on('connection', function (ConnectionInterface $peer) use ($data) { - $peer->end($data); - }); - - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - // await data from client until it closes - $received = $this->buffer($client, $this->loop, self::TIMEOUT); - - $this->assertEquals($data, $received); - } - - public function testConnectToServerWhichSendsDataWithoutEndingReceivesAllData() - { - $data = str_repeat('c', 100000); - $this->server->on('connection', function (ConnectionInterface $peer) use ($data) { - $peer->write($data); - }); - - $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT); - /* @var $client ConnectionInterface */ - - // buffer incoming data for 0.1s (should be plenty of time) - $received = ''; - $client->on('data', function ($chunk) use (&$received) { - $received .= $chunk; - }); - Block\sleep(0.1, $this->loop); - - $client->close(); - - $this->assertEquals($data, $received); - } - - private function createPromiseForEvent(EventEmitterInterface $emitter, $event, $fn) - { - return new Promise(function ($resolve) use ($emitter, $event, $fn) { - $emitter->on($event, function () use ($resolve, $fn) { - $resolve(call_user_func_array($fn, func_get_args())); - }); - }); - } -} diff --git a/vendor/react/socket/tests/SecureServerTest.php b/vendor/react/socket/tests/SecureServerTest.php deleted file mode 100644 index 92c641f..0000000 --- a/vendor/react/socket/tests/SecureServerTest.php +++ /dev/null @@ -1,105 +0,0 @@ -markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - } - - public function testGetAddressWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('getAddress')->willReturn('tcp://127.0.0.1:1234'); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $server = new SecureServer($tcp, $loop, array()); - - $this->assertEquals('tls://127.0.0.1:1234', $server->getAddress()); - } - - public function testGetAddressWillReturnNullIfTcpServerReturnsNull() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('getAddress')->willReturn(null); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $server = new SecureServer($tcp, $loop, array()); - - $this->assertNull($server->getAddress()); - } - - public function testPauseWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('pause'); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $server = new SecureServer($tcp, $loop, array()); - - $server->pause(); - } - - public function testResumeWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('resume'); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $server = new SecureServer($tcp, $loop, array()); - - $server->resume(); - } - - public function testCloseWillBePassedThroughToTcpServer() - { - $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); - $tcp->expects($this->once())->method('close'); - - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $server = new SecureServer($tcp, $loop, array()); - - $server->close(); - } - - public function testConnectionWillBeEndedWithErrorIfItIsNotAStream() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $tcp = new TcpServer(0, $loop); - - $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock(); - $connection->expects($this->once())->method('end'); - - $server = new SecureServer($tcp, $loop, array()); - - $server->on('error', $this->expectCallableOnce()); - - $tcp->emit('connection', array($connection)); - } - - public function testSocketErrorWillBeForwarded() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $tcp = new TcpServer(0, $loop); - - $server = new SecureServer($tcp, $loop, array()); - - $server->on('error', $this->expectCallableOnce()); - - $tcp->emit('error', array(new \RuntimeException('test'))); - } -} diff --git a/vendor/react/socket/tests/ServerTest.php b/vendor/react/socket/tests/ServerTest.php deleted file mode 100644 index 14fdb2c..0000000 --- a/vendor/react/socket/tests/ServerTest.php +++ /dev/null @@ -1,173 +0,0 @@ -assertNotEquals(0, $server->getAddress()); - $server->close(); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorThrowsForInvalidUri() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $server = new Server('invalid URI', $loop); - } - - public function testConstructorCreatesExpectedTcpServer() - { - $loop = Factory::create(); - - $server = new Server(0, $loop); - - $connector = new TcpConnector($loop); - $connector->connect($server->getAddress()) - ->then($this->expectCallableOnce(), $this->expectCallableNever()); - - $connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT); - - $connection->close(); - $server->close(); - } - - public function testConstructorCreatesExpectedUnixServer() - { - $loop = Factory::create(); - - $server = new Server($this->getRandomSocketUri(), $loop); - - $connector = new UnixConnector($loop); - $connector->connect($server->getAddress()) - ->then($this->expectCallableOnce(), $this->expectCallableNever()); - - $connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT); - - $connection->close(); - $server->close(); - } - - public function testEmitsConnectionForNewConnection() - { - $loop = Factory::create(); - - $server = new Server(0, $loop); - $server->on('connection', $this->expectCallableOnce()); - - $client = stream_socket_client($server->getAddress()); - - Block\sleep(0.1, $loop); - } - - public function testDoesNotEmitConnectionForNewConnectionToPausedServer() - { - $loop = Factory::create(); - - $server = new Server(0, $loop); - $server->pause(); - $server->on('connection', $this->expectCallableNever()); - - $client = stream_socket_client($server->getAddress()); - - Block\sleep(0.1, $loop); - } - - public function testDoesEmitConnectionForNewConnectionToResumedServer() - { - $loop = Factory::create(); - - $server = new Server(0, $loop); - $server->pause(); - $server->on('connection', $this->expectCallableOnce()); - - $client = stream_socket_client($server->getAddress()); - - Block\sleep(0.1, $loop); - - $server->resume(); - Block\sleep(0.1, $loop); - } - - public function testDoesNotAllowConnectionToClosedServer() - { - $loop = Factory::create(); - - $server = new Server(0, $loop); - $server->on('connection', $this->expectCallableNever()); - $address = $server->getAddress(); - $server->close(); - - $client = @stream_socket_client($address); - - Block\sleep(0.1, $loop); - - $this->assertFalse($client); - } - - public function testEmitsConnectionWithInheritedContextOptions() - { - if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) { - // https://3v4l.org/hB4Tc - $this->markTestSkipped('Not supported on legacy HHVM < 3.13'); - } - - $loop = Factory::create(); - - $server = new Server(0, $loop, array( - 'backlog' => 4 - )); - - $all = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$all) { - $all = stream_context_get_options($conn->stream); - }); - - $client = stream_socket_client($server->getAddress()); - - Block\sleep(0.1, $loop); - - $this->assertEquals(array('socket' => array('backlog' => 4)), $all); - } - - public function testDoesNotEmitSecureConnectionForNewPlainConnection() - { - if (!function_exists('stream_socket_enable_crypto')) { - $this->markTestSkipped('Not supported on your platform (outdated HHVM?)'); - } - - $loop = Factory::create(); - - $server = new Server('tls://127.0.0.1:0', $loop, array( - 'tls' => array( - 'local_cert' => __DIR__ . '/../examples/localhost.pem' - ) - )); - $server->on('connection', $this->expectCallableNever()); - - $client = stream_socket_client(str_replace('tls://', '', $server->getAddress())); - - Block\sleep(0.1, $loop); - } - - private function getRandomSocketUri() - { - return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock'; - } -} diff --git a/vendor/react/socket/tests/Stub/CallableStub.php b/vendor/react/socket/tests/Stub/CallableStub.php deleted file mode 100644 index 1b197eb..0000000 --- a/vendor/react/socket/tests/Stub/CallableStub.php +++ /dev/null @@ -1,10 +0,0 @@ -data .= $data; - - return true; - } - - public function end($data = null) - { - } - - public function close() - { - } - - public function getData() - { - return $this->data; - } - - public function getRemoteAddress() - { - return '127.0.0.1'; - } -} diff --git a/vendor/react/socket/tests/Stub/ServerStub.php b/vendor/react/socket/tests/Stub/ServerStub.php deleted file mode 100644 index d9e74f4..0000000 --- a/vendor/react/socket/tests/Stub/ServerStub.php +++ /dev/null @@ -1,18 +0,0 @@ -connect('127.0.0.1:9999') - ->then($this->expectCallableNever(), $this->expectCallableOnce()); - - $loop->run(); - } - - /** @test */ - public function connectionToTcpServerShouldAddResourceToLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new TcpConnector($loop); - - $server = new TcpServer(0, $loop); - - $valid = false; - $loop->expects($this->once())->method('addWriteStream')->with($this->callback(function ($arg) use (&$valid) { - $valid = is_resource($arg); - return true; - })); - $connector->connect($server->getAddress()); - - $this->assertTrue($valid); - } - - /** @test */ - public function connectionToTcpServerShouldSucceed() - { - $loop = Factory::create(); - - $server = new TcpServer(9999, $loop); - $server->on('connection', $this->expectCallableOnce()); - $server->on('connection', array($server, 'close')); - - $connector = new TcpConnector($loop); - - $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT); - - $this->assertInstanceOf('React\Socket\ConnectionInterface', $connection); - - $connection->close(); - } - - /** @test */ - public function connectionToTcpServerShouldSucceedWithRemoteAdressSameAsTarget() - { - $loop = Factory::create(); - - $server = new TcpServer(9999, $loop); - $server->on('connection', array($server, 'close')); - - $connector = new TcpConnector($loop); - - $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT); - /* @var $connection ConnectionInterface */ - - $this->assertEquals('tcp://127.0.0.1:9999', $connection->getRemoteAddress()); - - $connection->close(); - } - - /** @test */ - public function connectionToTcpServerShouldSucceedWithLocalAdressOnLocalhost() - { - $loop = Factory::create(); - - $server = new TcpServer(9999, $loop); - $server->on('connection', array($server, 'close')); - - $connector = new TcpConnector($loop); - - $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT); - /* @var $connection ConnectionInterface */ - - $this->assertContains('tcp://127.0.0.1:', $connection->getLocalAddress()); - $this->assertNotEquals('tcp://127.0.0.1:9999', $connection->getLocalAddress()); - - $connection->close(); - } - - /** @test */ - public function connectionToTcpServerShouldSucceedWithNullAddressesAfterConnectionClosed() - { - $loop = Factory::create(); - - $server = new TcpServer(9999, $loop); - $server->on('connection', array($server, 'close')); - - $connector = new TcpConnector($loop); - - $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT); - /* @var $connection ConnectionInterface */ - - $connection->close(); - - $this->assertNull($connection->getRemoteAddress()); - $this->assertNull($connection->getLocalAddress()); - } - - /** @test */ - public function connectionToTcpServerWillCloseWhenOtherSideCloses() - { - $loop = Factory::create(); - - // immediately close connection and server once connection is in - $server = new TcpServer(0, $loop); - $server->on('connection', function (ConnectionInterface $conn) use ($server) { - $conn->close(); - $server->close(); - }); - - $once = $this->expectCallableOnce(); - $connector = new TcpConnector($loop); - $connector->connect($server->getAddress())->then(function (ConnectionInterface $conn) use ($once) { - $conn->write('hello'); - $conn->on('close', $once); - }); - - $loop->run(); - } - - /** @test */ - public function connectionToEmptyIp6PortShouldFail() - { - $loop = Factory::create(); - - $connector = new TcpConnector($loop); - $connector - ->connect('[::1]:9999') - ->then($this->expectCallableNever(), $this->expectCallableOnce()); - - $loop->run(); - } - - /** @test */ - public function connectionToIp6TcpServerShouldSucceed() - { - $loop = Factory::create(); - - try { - $server = new TcpServer('[::1]:9999', $loop); - } catch (\Exception $e) { - $this->markTestSkipped('Unable to start IPv6 server socket (IPv6 not supported on this system?)'); - } - - $server->on('connection', $this->expectCallableOnce()); - $server->on('connection', array($server, 'close')); - - $connector = new TcpConnector($loop); - - $connection = Block\await($connector->connect('[::1]:9999'), $loop, self::TIMEOUT); - /* @var $connection ConnectionInterface */ - - $this->assertEquals('tcp://[::1]:9999', $connection->getRemoteAddress()); - - $this->assertContains('tcp://[::1]:', $connection->getLocalAddress()); - $this->assertNotEquals('tcp://[::1]:9999', $connection->getLocalAddress()); - - $connection->close(); - } - - /** @test */ - public function connectionToHostnameShouldFailImmediately() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $connector = new TcpConnector($loop); - $connector->connect('www.google.com:80')->then( - $this->expectCallableNever(), - $this->expectCallableOnce() - ); - } - - /** @test */ - public function connectionToInvalidPortShouldFailImmediately() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $connector = new TcpConnector($loop); - $connector->connect('255.255.255.255:12345678')->then( - $this->expectCallableNever(), - $this->expectCallableOnce() - ); - } - - /** @test */ - public function connectionToInvalidSchemeShouldFailImmediately() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - - $connector = new TcpConnector($loop); - $connector->connect('tls://google.com:443')->then( - $this->expectCallableNever(), - $this->expectCallableOnce() - ); - } - - /** @test */ - public function cancellingConnectionShouldRemoveResourceFromLoopAndCloseResource() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $connector = new TcpConnector($loop); - - $server = new TcpServer(0, $loop); - $server->on('connection', $this->expectCallableNever()); - - $loop->expects($this->once())->method('addWriteStream'); - $promise = $connector->connect($server->getAddress()); - - $resource = null; - $valid = false; - $loop->expects($this->once())->method('removeWriteStream')->with($this->callback(function ($arg) use (&$resource, &$valid) { - $resource = $arg; - $valid = is_resource($arg); - return true; - })); - $promise->cancel(); - - // ensure that this was a valid resource during the removeWriteStream() call - $this->assertTrue($valid); - - // ensure that this resource should now be closed after the cancel() call - $this->assertInternalType('resource', $resource); - $this->assertFalse(is_resource($resource)); - } - - /** @test */ - public function cancellingConnectionShouldRejectPromise() - { - $loop = Factory::create(); - $connector = new TcpConnector($loop); - - $server = new TcpServer(0, $loop); - - $promise = $connector->connect($server->getAddress()); - $promise->cancel(); - - $this->setExpectedException('RuntimeException', 'Cancelled'); - Block\await($promise, $loop); - } -} diff --git a/vendor/react/socket/tests/TcpServerTest.php b/vendor/react/socket/tests/TcpServerTest.php deleted file mode 100644 index 72b3c28..0000000 --- a/vendor/react/socket/tests/TcpServerTest.php +++ /dev/null @@ -1,285 +0,0 @@ -loop = $this->createLoop(); - $this->server = new TcpServer(0, $this->loop); - - $this->port = parse_url($this->server->getAddress(), PHP_URL_PORT); - } - - /** - * @covers React\Socket\TcpServer::handleConnection - */ - public function testConnection() - { - $client = stream_socket_client('tcp://localhost:'.$this->port); - - $this->server->on('connection', $this->expectCallableOnce()); - - $this->tick(); - } - - /** - * @covers React\Socket\TcpServer::handleConnection - */ - public function testConnectionWithManyClients() - { - $client1 = stream_socket_client('tcp://localhost:'.$this->port); - $client2 = stream_socket_client('tcp://localhost:'.$this->port); - $client3 = stream_socket_client('tcp://localhost:'.$this->port); - - $this->server->on('connection', $this->expectCallableExactly(3)); - $this->tick(); - $this->tick(); - $this->tick(); - } - - public function testDataEventWillNotBeEmittedWhenClientSendsNoData() - { - $client = stream_socket_client('tcp://localhost:'.$this->port); - - $mock = $this->expectCallableNever(); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('data', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testDataWillBeEmittedWithDataClientSends() - { - $client = stream_socket_client('tcp://localhost:'.$this->port); - - fwrite($client, "foo\n"); - - $mock = $this->expectCallableOnceWith("foo\n"); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('data', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testDataWillBeEmittedEvenWhenClientShutsDownAfterSending() - { - $client = stream_socket_client('tcp://localhost:' . $this->port); - fwrite($client, "foo\n"); - stream_socket_shutdown($client, STREAM_SHUT_WR); - - $mock = $this->expectCallableOnceWith("foo\n"); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('data', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testLoopWillEndWhenServerIsClosed() - { - // explicitly unset server because we already call close() - $this->server->close(); - $this->server = null; - - $this->loop->run(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testCloseTwiceIsNoOp() - { - $this->server->close(); - $this->server->close(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testGetAddressAfterCloseReturnsNull() - { - $this->server->close(); - $this->assertNull($this->server->getAddress()); - } - - public function testLoopWillEndWhenServerIsClosedAfterSingleConnection() - { - $client = stream_socket_client('tcp://localhost:' . $this->port); - - // explicitly unset server because we only accept a single connection - // and then already call close() - $server = $this->server; - $this->server = null; - - $server->on('connection', function ($conn) use ($server) { - $conn->close(); - $server->close(); - }); - - $this->loop->run(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmounts() - { - $client = stream_socket_client('tcp://localhost:' . $this->port); - $stream = new DuplexResourceStream($client, $this->loop); - - $bytes = 1024 * 1024; - $stream->end(str_repeat('*', $bytes)); - - $mock = $this->expectCallableOnce(); - - // explicitly unset server because we only accept a single connection - // and then already call close() - $server = $this->server; - $this->server = null; - - $received = 0; - $server->on('connection', function ($conn) use ($mock, &$received, $server) { - // count number of bytes received - $conn->on('data', function ($data) use (&$received) { - $received += strlen($data); - }); - - $conn->on('end', $mock); - - // do not await any further connections in order to let the loop terminate - $server->close(); - }); - - $this->loop->run(); - - $this->assertEquals($bytes, $received); - } - - public function testConnectionDoesNotEndWhenClientDoesNotClose() - { - $client = stream_socket_client('tcp://localhost:'.$this->port); - - $mock = $this->expectCallableNever(); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('end', $mock); - }); - $this->tick(); - $this->tick(); - } - - /** - * @covers React\Socket\Connection::end - */ - public function testConnectionDoesEndWhenClientCloses() - { - $client = stream_socket_client('tcp://localhost:'.$this->port); - - fclose($client); - - $mock = $this->expectCallableOnce(); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('end', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testCtorAddsResourceToLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('addReadStream'); - - $server = new TcpServer(0, $loop); - } - - public function testResumeWithoutPauseIsNoOp() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('addReadStream'); - - $server = new TcpServer(0, $loop); - $server->resume(); - } - - public function testPauseRemovesResourceFromLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('removeReadStream'); - - $server = new TcpServer(0, $loop); - $server->pause(); - } - - public function testPauseAfterPauseIsNoOp() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('removeReadStream'); - - $server = new TcpServer(0, $loop); - $server->pause(); - $server->pause(); - } - - public function testCloseRemovesResourceFromLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('removeReadStream'); - - $server = new TcpServer(0, $loop); - $server->close(); - } - - /** - * @expectedException RuntimeException - */ - public function testListenOnBusyPortThrows() - { - if (DIRECTORY_SEPARATOR === '\\') { - $this->markTestSkipped('Windows supports listening on same port multiple times'); - } - - $another = new TcpServer($this->port, $this->loop); - } - - /** - * @covers React\Socket\TcpServer::close - */ - public function tearDown() - { - if ($this->server) { - $this->server->close(); - } - } - - private function tick() - { - Block\sleep(0, $this->loop); - } -} diff --git a/vendor/react/socket/tests/TestCase.php b/vendor/react/socket/tests/TestCase.php deleted file mode 100644 index e87fc2f..0000000 --- a/vendor/react/socket/tests/TestCase.php +++ /dev/null @@ -1,101 +0,0 @@ -createCallableMock(); - $mock - ->expects($this->exactly($amount)) - ->method('__invoke'); - - return $mock; - } - - protected function expectCallableOnce() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke'); - - return $mock; - } - - protected function expectCallableOnceWith($value) - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($value); - - return $mock; - } - - protected function expectCallableNever() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->never()) - ->method('__invoke'); - - return $mock; - } - - protected function createCallableMock() - { - return $this->getMockBuilder('React\Tests\Socket\Stub\CallableStub')->getMock(); - } - - protected function buffer(ReadableStreamInterface $stream, LoopInterface $loop, $timeout) - { - if (!$stream->isReadable()) { - return ''; - } - - return Block\await(new Promise( - function ($resolve, $reject) use ($stream) { - $buffer = ''; - $stream->on('data', function ($chunk) use (&$buffer) { - $buffer .= $chunk; - }); - - $stream->on('error', $reject); - - $stream->on('close', function () use (&$buffer, $resolve) { - $resolve($buffer); - }); - }, - function () use ($stream) { - $stream->close(); - throw new \RuntimeException(); - } - ), $loop, $timeout); - } - - public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null) - { - if (method_exists($this, 'expectException')) { - // PHPUnit 5+ - $this->expectException($exception); - if ($exceptionMessage !== '') { - $this->expectExceptionMessage($exceptionMessage); - } - if ($exceptionCode !== null) { - $this->expectExceptionCode($exceptionCode); - } - } else { - // legacy PHPUnit 4 - parent::setExpectedException($exception, $exceptionMessage, $exceptionCode); - } - } -} diff --git a/vendor/react/socket/tests/TimeoutConnectorTest.php b/vendor/react/socket/tests/TimeoutConnectorTest.php deleted file mode 100644 index 64787d9..0000000 --- a/vendor/react/socket/tests/TimeoutConnectorTest.php +++ /dev/null @@ -1,103 +0,0 @@ -getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise)); - - $loop = Factory::create(); - - $timeout = new TimeoutConnector($connector, 0.01, $loop); - - $timeout->connect('google.com:80')->then( - $this->expectCallableNever(), - $this->expectCallableOnce() - ); - - $loop->run(); - } - - public function testRejectsWhenConnectorRejects() - { - $promise = Promise\reject(new \RuntimeException()); - - $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise)); - - $loop = Factory::create(); - - $timeout = new TimeoutConnector($connector, 5.0, $loop); - - $timeout->connect('google.com:80')->then( - $this->expectCallableNever(), - $this->expectCallableOnce() - ); - - $loop->run(); - } - - public function testResolvesWhenConnectorResolves() - { - $promise = Promise\resolve(); - - $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise)); - - $loop = Factory::create(); - - $timeout = new TimeoutConnector($connector, 5.0, $loop); - - $timeout->connect('google.com:80')->then( - $this->expectCallableOnce(), - $this->expectCallableNever() - ); - - $loop->run(); - } - - public function testRejectsAndCancelsPendingPromiseOnTimeout() - { - $promise = new Promise\Promise(function () { }, $this->expectCallableOnce()); - - $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise)); - - $loop = Factory::create(); - - $timeout = new TimeoutConnector($connector, 0.01, $loop); - - $timeout->connect('google.com:80')->then( - $this->expectCallableNever(), - $this->expectCallableOnce() - ); - - $loop->run(); - } - - public function testCancelsPendingPromiseOnCancel() - { - $promise = new Promise\Promise(function () { }, function () { throw new \Exception(); }); - - $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); - $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise)); - - $loop = Factory::create(); - - $timeout = new TimeoutConnector($connector, 0.01, $loop); - - $out = $timeout->connect('google.com:80'); - $out->cancel(); - - $out->then($this->expectCallableNever(), $this->expectCallableOnce()); - } -} diff --git a/vendor/react/socket/tests/UnixConnectorTest.php b/vendor/react/socket/tests/UnixConnectorTest.php deleted file mode 100644 index 1564064..0000000 --- a/vendor/react/socket/tests/UnixConnectorTest.php +++ /dev/null @@ -1,64 +0,0 @@ -loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $this->connector = new UnixConnector($this->loop); - } - - public function testInvalid() - { - $promise = $this->connector->connect('google.com:80'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testInvalidScheme() - { - $promise = $this->connector->connect('tcp://google.com:80'); - $promise->then(null, $this->expectCallableOnce()); - } - - public function testValid() - { - // random unix domain socket path - $path = sys_get_temp_dir() . '/test' . uniqid() . '.sock'; - - // temporarily create unix domain socket server to connect to - $server = stream_socket_server('unix://' . $path, $errno, $errstr); - - // skip test if we can not create a test server (Windows etc.) - if (!$server) { - $this->markTestSkipped('Unable to create socket "' . $path . '": ' . $errstr . '(' . $errno .')'); - return; - } - - // tests succeeds if we get notified of successful connection - $promise = $this->connector->connect($path); - $promise->then($this->expectCallableOnce()); - - // remember remote and local address of this connection and close again - $remote = $local = false; - $promise->then(function(ConnectionInterface $conn) use (&$remote, &$local) { - $remote = $conn->getRemoteAddress(); - $local = $conn->getLocalAddress(); - $conn->close(); - }); - - // clean up server - fclose($server); - unlink($path); - - $this->assertNull($local); - $this->assertEquals('unix://' . $path, $remote); - } -} diff --git a/vendor/react/socket/tests/UnixServerTest.php b/vendor/react/socket/tests/UnixServerTest.php deleted file mode 100644 index 10f7e4f..0000000 --- a/vendor/react/socket/tests/UnixServerTest.php +++ /dev/null @@ -1,283 +0,0 @@ -loop = Factory::create(); - $this->uds = $this->getRandomSocketUri(); - $this->server = new UnixServer($this->uds, $this->loop); - } - - /** - * @covers React\Socket\UnixServer::handleConnection - */ - public function testConnection() - { - $client = stream_socket_client($this->uds); - - $this->server->on('connection', $this->expectCallableOnce()); - $this->tick(); - } - - /** - * @covers React\Socket\UnixServer::handleConnection - */ - public function testConnectionWithManyClients() - { - $client1 = stream_socket_client($this->uds); - $client2 = stream_socket_client($this->uds); - $client3 = stream_socket_client($this->uds); - - $this->server->on('connection', $this->expectCallableExactly(3)); - $this->tick(); - $this->tick(); - $this->tick(); - } - - public function testDataEventWillNotBeEmittedWhenClientSendsNoData() - { - $client = stream_socket_client($this->uds); - - $mock = $this->expectCallableNever(); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('data', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testDataWillBeEmittedWithDataClientSends() - { - $client = stream_socket_client($this->uds); - - fwrite($client, "foo\n"); - - $mock = $this->expectCallableOnceWith("foo\n"); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('data', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testDataWillBeEmittedEvenWhenClientShutsDownAfterSending() - { - $client = stream_socket_client($this->uds); - fwrite($client, "foo\n"); - stream_socket_shutdown($client, STREAM_SHUT_WR); - - $mock = $this->expectCallableOnceWith("foo\n"); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('data', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testLoopWillEndWhenServerIsClosed() - { - // explicitly unset server because we already call close() - $this->server->close(); - $this->server = null; - - $this->loop->run(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testCloseTwiceIsNoOp() - { - $this->server->close(); - $this->server->close(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testGetAddressAfterCloseReturnsNull() - { - $this->server->close(); - $this->assertNull($this->server->getAddress()); - } - - public function testLoopWillEndWhenServerIsClosedAfterSingleConnection() - { - $client = stream_socket_client($this->uds); - - // explicitly unset server because we only accept a single connection - // and then already call close() - $server = $this->server; - $this->server = null; - - $server->on('connection', function ($conn) use ($server) { - $conn->close(); - $server->close(); - }); - - $this->loop->run(); - - // if we reach this, then everything is good - $this->assertNull(null); - } - - public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmounts() - { - $client = stream_socket_client($this->uds); - $stream = new DuplexResourceStream($client, $this->loop); - - $bytes = 1024 * 1024; - $stream->end(str_repeat('*', $bytes)); - - $mock = $this->expectCallableOnce(); - - // explicitly unset server because we only accept a single connection - // and then already call close() - $server = $this->server; - $this->server = null; - - $received = 0; - $server->on('connection', function ($conn) use ($mock, &$received, $server) { - // count number of bytes received - $conn->on('data', function ($data) use (&$received) { - $received += strlen($data); - }); - - $conn->on('end', $mock); - - // do not await any further connections in order to let the loop terminate - $server->close(); - }); - - $this->loop->run(); - - $this->assertEquals($bytes, $received); - } - - public function testConnectionDoesNotEndWhenClientDoesNotClose() - { - $client = stream_socket_client($this->uds); - - $mock = $this->expectCallableNever(); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('end', $mock); - }); - $this->tick(); - $this->tick(); - } - - /** - * @covers React\Socket\Connection::end - */ - public function testConnectionDoesEndWhenClientCloses() - { - $client = stream_socket_client($this->uds); - - fclose($client); - - $mock = $this->expectCallableOnce(); - - $this->server->on('connection', function ($conn) use ($mock) { - $conn->on('end', $mock); - }); - $this->tick(); - $this->tick(); - } - - public function testCtorAddsResourceToLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('addReadStream'); - - $server = new UnixServer($this->getRandomSocketUri(), $loop); - } - - public function testResumeWithoutPauseIsNoOp() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('addReadStream'); - - $server = new UnixServer($this->getRandomSocketUri(), $loop); - $server->resume(); - } - - public function testPauseRemovesResourceFromLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('removeReadStream'); - - $server = new UnixServer($this->getRandomSocketUri(), $loop); - $server->pause(); - } - - public function testPauseAfterPauseIsNoOp() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('removeReadStream'); - - $server = new UnixServer($this->getRandomSocketUri(), $loop); - $server->pause(); - $server->pause(); - } - - public function testCloseRemovesResourceFromLoop() - { - $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); - $loop->expects($this->once())->method('removeReadStream'); - - $server = new UnixServer($this->getRandomSocketUri(), $loop); - $server->close(); - } - - /** - * @expectedException RuntimeException - */ - public function testListenOnBusyPortThrows() - { - if (DIRECTORY_SEPARATOR === '\\') { - $this->markTestSkipped('Windows supports listening on same port multiple times'); - } - - $another = new UnixServer($this->uds, $this->loop); - } - - /** - * @covers React\Socket\UnixServer::close - */ - public function tearDown() - { - if ($this->server) { - $this->server->close(); - } - } - - private function getRandomSocketUri() - { - return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock'; - } - - private function tick() - { - Block\sleep(0, $this->loop); - } -} diff --git a/vendor/symfony/http-foundation/.gitattributes b/vendor/symfony/http-foundation/.gitattributes new file mode 100644 index 0000000..ebb9287 --- /dev/null +++ b/vendor/symfony/http-foundation/.gitattributes @@ -0,0 +1,3 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitignore export-ignore diff --git a/vendor/symfony/http-foundation/.gitignore b/vendor/symfony/http-foundation/.gitignore deleted file mode 100644 index c49a5d8..0000000 --- a/vendor/symfony/http-foundation/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/vendor/symfony/http-foundation/AcceptHeader.php b/vendor/symfony/http-foundation/AcceptHeader.php index d174026..bbbd62a 100644 --- a/vendor/symfony/http-foundation/AcceptHeader.php +++ b/vendor/symfony/http-foundation/AcceptHeader.php @@ -24,7 +24,7 @@ class AcceptHeader /** * @var AcceptHeaderItem[] */ - private $items = array(); + private $items = []; /** * @var bool @@ -52,12 +52,17 @@ public static function fromString($headerValue) { $index = 0; - return new self(array_map(function ($itemValue) use (&$index) { - $item = AcceptHeaderItem::fromString($itemValue); + $parts = HeaderUtils::split((string) $headerValue, ',;='); + + return new self(array_map(function ($subParts) use (&$index) { + $part = array_shift($subParts); + $attributes = HeaderUtils::combine($subParts); + + $item = new AcceptHeaderItem($part[0], $attributes); $item->setIndex($index++); return $item; - }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE))); + }, $parts)); } /** @@ -91,7 +96,7 @@ public function has($value) */ public function get($value) { - return isset($this->items[$value]) ? $this->items[$value] : null; + return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null; } /** @@ -148,7 +153,7 @@ public function first() /** * Sorts items by descending quality. */ - private function sort() + private function sort(): void { if (!$this->sorted) { uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) { diff --git a/vendor/symfony/http-foundation/AcceptHeaderItem.php b/vendor/symfony/http-foundation/AcceptHeaderItem.php index c950657..954aac6 100644 --- a/vendor/symfony/http-foundation/AcceptHeaderItem.php +++ b/vendor/symfony/http-foundation/AcceptHeaderItem.php @@ -21,13 +21,9 @@ class AcceptHeaderItem private $value; private $quality = 1.0; private $index = 0; - private $attributes = array(); + private $attributes = []; - /** - * @param string $value - * @param array $attributes - */ - public function __construct($value, array $attributes = array()) + public function __construct(string $value, array $attributes = []) { $this->value = $value; foreach ($attributes as $name => $value) { @@ -44,24 +40,12 @@ public function __construct($value, array $attributes = array()) */ public static function fromString($itemValue) { - $bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); - $value = array_shift($bits); - $attributes = array(); - - $lastNullAttribute = null; - foreach ($bits as $bit) { - if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ('"' === $start || '\'' === $start)) { - $attributes[$lastNullAttribute] = substr($bit, 1, -1); - } elseif ('=' === $end) { - $lastNullAttribute = $bit = substr($bit, 0, -1); - $attributes[$bit] = null; - } else { - $parts = explode('=', $bit); - $attributes[$parts[0]] = isset($parts[1]) && \strlen($parts[1]) > 0 ? $parts[1] : ''; - } - } + $parts = HeaderUtils::split($itemValue, ';='); + + $part = array_shift($parts); + $attributes = HeaderUtils::combine($parts); - return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes); + return new self($part[0], $attributes); } /** @@ -73,9 +57,7 @@ public function __toString() { $string = $this->value.($this->quality < 1 ? ';q='.$this->quality : ''); if (\count($this->attributes) > 0) { - $string .= ';'.implode(';', array_map(function ($name, $value) { - return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value); - }, array_keys($this->attributes), $this->attributes)); + $string .= '; '.HeaderUtils::toString($this->attributes, ';'); } return $string; diff --git a/vendor/symfony/http-foundation/ApacheRequest.php b/vendor/symfony/http-foundation/ApacheRequest.php index 4e99186..f189cde 100644 --- a/vendor/symfony/http-foundation/ApacheRequest.php +++ b/vendor/symfony/http-foundation/ApacheRequest.php @@ -11,9 +11,13 @@ namespace Symfony\Component\HttpFoundation; +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ApacheRequest::class, Request::class), E_USER_DEPRECATED); + /** * Request represents an HTTP request from an Apache server. * + * @deprecated since Symfony 4.4. Use the Request class instead. + * * @author Fabien Potencier */ class ApacheRequest extends Request diff --git a/vendor/symfony/http-foundation/BinaryFileResponse.php b/vendor/symfony/http-foundation/BinaryFileResponse.php index 9702327..7bdbf4d 100644 --- a/vendor/symfony/http-foundation/BinaryFileResponse.php +++ b/vendor/symfony/http-foundation/BinaryFileResponse.php @@ -44,7 +44,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, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $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); @@ -66,7 +66,7 @@ public function __construct($file, $status = 200, $headers = array(), $public = * * @return static */ - public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) { return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified); } @@ -204,7 +204,7 @@ public function prepare(Request $request) if (!$this->headers->has('Accept-Ranges')) { // Only accept ranges on safe HTTP methods - $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none'); + $this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none'); } if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) { @@ -217,29 +217,29 @@ public function prepare(Request $request) } if ('x-accel-redirect' === strtolower($type)) { // Do X-Accel-Mapping substitutions. - // @link http://wiki.nginx.org/X-accel#X-Accel-Redirect - foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) { - $mapping = explode('=', $mapping, 2); - - if (2 === \count($mapping)) { - $pathPrefix = trim($mapping[0]); - $location = trim($mapping[1]); - - if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) { - $path = $location.substr($path, \strlen($pathPrefix)); - break; - } + // @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect + $parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',='); + foreach ($parts as $part) { + list($pathPrefix, $location) = $part; + if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) { + $path = $location.substr($path, \strlen($pathPrefix)); + // Only set X-Accel-Redirect header if a valid URI can be produced + // as nginx does not serve arbitrary file paths. + $this->headers->set($type, $path); + $this->maxlen = 0; + break; } } + } else { + $this->headers->set($type, $path); + $this->maxlen = 0; } - $this->headers->set($type, $path); - $this->maxlen = 0; } elseif ($request->headers->has('Range')) { // Process the range headers. if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) { $range = $request->headers->get('Range'); - list($start, $end) = explode('-', substr($range, 6), 2) + array(0); + list($start, $end) = explode('-', substr($range, 6), 2) + [0]; $end = ('' === $end) ? $fileSize - 1 : (int) $end; @@ -269,7 +269,7 @@ public function prepare(Request $request) return $this; } - private function hasValidIfRangeHeader($header) + private function hasValidIfRangeHeader(?string $header): bool { if ($this->getEtag() === $header) { return true; @@ -305,7 +305,7 @@ public function sendContent() fclose($out); fclose($file); - if ($this->deleteFileAfterSend) { + if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) { unlink($this->file->getPathname()); } @@ -322,12 +322,12 @@ public function setContent($content) if (null !== $content) { throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.'); } + + return $this; } /** * {@inheritdoc} - * - * @return false */ public function getContent() { @@ -343,14 +343,14 @@ public static function trustXSendfileTypeHeader() } /** - * If this is set to true, the file will be unlinked after the request is send + * If this is set to true, the file will be unlinked after the request is sent * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used. * * @param bool $shouldDelete * * @return $this */ - public function deleteFileAfterSend($shouldDelete) + public function deleteFileAfterSend($shouldDelete = true) { $this->deleteFileAfterSend = $shouldDelete; diff --git a/vendor/symfony/http-foundation/CHANGELOG.md b/vendor/symfony/http-foundation/CHANGELOG.md index 97b279d..3fa73a2 100644 --- a/vendor/symfony/http-foundation/CHANGELOG.md +++ b/vendor/symfony/http-foundation/CHANGELOG.md @@ -1,12 +1,84 @@ CHANGELOG ========= -3.4.14 ------- +4.4.0 +----- + + * passing arguments to `Request::isMethodSafe()` is deprecated. + * `ApacheRequest` is deprecated, use the `Request` class instead. + * passing a third argument to `HeaderBag::get()` is deprecated, use method `all()` instead + * [BC BREAK] `PdoSessionHandler` with MySQL changed the type of the lifetime column, + make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to + update your database. + * `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column, + make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database + to speed up garbage collection of expired sessions. + * added `SessionHandlerFactory` to create session handlers with a DSN + * added `IpUtils::anonymize()` to help with GDPR compliance. + +4.3.0 +----- + + * added PHPUnit constraints: `RequestAttributeValueSame`, `ResponseCookieValueSame`, `ResponseHasCookie`, + `ResponseHasHeader`, `ResponseHeaderSame`, `ResponseIsRedirected`, `ResponseIsSuccessful`, and `ResponseStatusCodeSame` + * deprecated `MimeTypeGuesserInterface` and `ExtensionGuesserInterface` in favor of `Symfony\Component\Mime\MimeTypesInterface`. + * deprecated `MimeType` and `MimeTypeExtensionGuesser` in favor of `Symfony\Component\Mime\MimeTypes`. + * deprecated `FileBinaryMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileBinaryMimeTypeGuesser`. + * deprecated `FileinfoMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileinfoMimeTypeGuesser`. + * added `UrlHelper` that allows to get an absolute URL and a relative path for a given path + +4.2.0 +----- + + * the default value of the "$secure" and "$samesite" arguments of Cookie's constructor + will respectively change from "false" to "null" and from "null" to "lax" in Symfony + 5.0, you should define their values explicitly or use "Cookie::create()" instead. + * added `matchPort()` in RequestMatcher + +4.1.3 +----- * [BC BREAK] Support for the IIS-only `X_ORIGINAL_URL` and `X_REWRITE_URL` HTTP headers has been dropped for security reasons. +4.1.0 +----- + + * Query string normalization uses `parse_str()` instead of custom parsing logic. + * Passing the file size to the constructor of the `UploadedFile` class is deprecated. + * The `getClientSize()` method of the `UploadedFile` class is deprecated. Use `getSize()` instead. + * added `RedisSessionHandler` to use Redis as a session storage + * The `get()` method of the `AcceptHeader` class now takes into account the + `*` and `*/*` default values (if they are present in the Accept HTTP header) + when looking for items. + * deprecated `Request::getSession()` when no session has been set. Use `Request::hasSession()` instead. + * added `CannotWriteFileException`, `ExtensionFileException`, `FormSizeFileException`, + `IniSizeFileException`, `NoFileException`, `NoTmpDirFileException`, `PartialFileException` to + handle failed `UploadedFile`. + * added `MigratingSessionHandler` for migrating between two session handlers without losing sessions + * added `HeaderUtils`. + +4.0.0 +----- + + * the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` + methods have been removed + * the `Request::HEADER_CLIENT_IP` constant has been removed, use + `Request::HEADER_X_FORWARDED_FOR` instead + * the `Request::HEADER_CLIENT_HOST` constant has been removed, use + `Request::HEADER_X_FORWARDED_HOST` instead + * the `Request::HEADER_CLIENT_PROTO` constant has been removed, use + `Request::HEADER_X_FORWARDED_PROTO` instead + * the `Request::HEADER_CLIENT_PORT` constant has been removed, use + `Request::HEADER_X_FORWARDED_PORT` instead + * checking for cacheable HTTP methods using the `Request::isMethodSafe()` + method (by not passing `false` as its argument) is not supported anymore and + throws a `\BadMethodCallException` + * the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes have been removed + * setting session save handlers that do not implement `\SessionHandlerInterface` in + `NativeSessionStorage::setSaveHandler()` is not supported anymore and throws a + `\TypeError` + 3.4.0 ----- @@ -21,7 +93,7 @@ CHANGELOG ----- * the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument, - see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info, + see https://symfony.com/doc/current/deployment/proxies.html for more info, * deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods, * added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown, disabling `Range` and `Content-Length` handling, switching to chunked encoding instead @@ -144,10 +216,10 @@ CHANGELOG * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This implementation is ESI compatible. * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire - behaviour of messages auto expiring after one page page load. Messages must + behavior of messages auto expiring after one page page load. Messages must be retrieved by `get()` or `all()`. * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate - attributes storage behaviour from 2.0.x (default). + attributes storage behavior from 2.0.x (default). * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for namespace session attributes. * Flash API can stores messages in an array so there may be multiple messages diff --git a/vendor/symfony/http-foundation/Cookie.php b/vendor/symfony/http-foundation/Cookie.php index c38aa40..1e22c74 100644 --- a/vendor/symfony/http-foundation/Cookie.php +++ b/vendor/symfony/http-foundation/Cookie.php @@ -18,6 +18,10 @@ */ class Cookie { + const SAMESITE_NONE = 'none'; + const SAMESITE_LAX = 'lax'; + const SAMESITE_STRICT = 'strict'; + protected $name; protected $value; protected $domain; @@ -25,11 +29,14 @@ class Cookie protected $path; protected $secure; protected $httpOnly; + private $raw; private $sameSite; + private $secureDefault = false; - const SAMESITE_LAX = 'lax'; - const SAMESITE_STRICT = 'strict'; + private static $reservedCharsList = "=,; \t\r\n\v\f"; + private static $reservedCharsFrom = ['=', ',', ';', ' ', "\t", "\r", "\n", "\v", "\f"]; + private static $reservedCharsTo = ['%3D', '%2C', '%3B', '%20', '%09', '%0D', '%0A', '%0B', '%0C']; /** * Creates cookie from raw header string. @@ -41,7 +48,7 @@ class Cookie */ public static function fromString($cookie, $decode = false) { - $data = array( + $data = [ 'expires' => 0, 'path' => '/', 'domain' => null, @@ -49,35 +56,26 @@ public static function fromString($cookie, $decode = false) 'httponly' => false, 'raw' => !$decode, 'samesite' => null, - ); - foreach (explode(';', $cookie) as $part) { - if (false === strpos($part, '=')) { - $key = trim($part); - $value = true; - } else { - list($key, $value) = explode('=', trim($part), 2); - $key = trim($key); - $value = trim($value); - } - if (!isset($data['name'])) { - $data['name'] = $decode ? urldecode($key) : $key; - $data['value'] = true === $value ? null : ($decode ? urldecode($value) : $value); - continue; - } - switch ($key = strtolower($key)) { - case 'name': - case 'value': - break; - case 'max-age': - $data['expires'] = time() + (int) $value; - break; - default: - $data[$key] = $value; - break; - } + ]; + + $parts = HeaderUtils::split($cookie, ';='); + $part = array_shift($parts); + + $name = $decode ? urldecode($part[0]) : $part[0]; + $value = isset($part[1]) ? ($decode ? urldecode($part[1]) : $part[1]) : null; + + $data = HeaderUtils::combine($parts) + $data; + + if (isset($data['max-age'])) { + $data['expires'] = time() + (int) $data['max-age']; } - return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']); + 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 + { + return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite); } /** @@ -86,17 +84,21 @@ public static function fromString($cookie, $decode = false) * @param int|string|\DateTimeInterface $expire The time the cookie expires * @param string $path The path on the server in which the cookie will be available on * @param string|null $domain The domain that the cookie is available to - * @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client + * @param bool|null $secure Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS * @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol * @param bool $raw Whether the cookie value should be sent with no url encoding * @param string|null $sameSite Whether the cookie will be available for cross-site requests * * @throws \InvalidArgumentException */ - public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null) + public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null) { + if (9 > \func_num_args()) { + @trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED); + } + // from PHP source code - if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + if ($raw && false !== strpbrk($name, self::$reservedCharsList)) { throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); } @@ -120,15 +122,17 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom $this->domain = $domain; $this->expire = 0 < $expire ? (int) $expire : 0; $this->path = empty($path) ? '/' : $path; - $this->secure = (bool) $secure; - $this->httpOnly = (bool) $httpOnly; - $this->raw = (bool) $raw; + $this->secure = $secure; + $this->httpOnly = $httpOnly; + $this->raw = $raw; - if (null !== $sameSite) { + if ('' === $sameSite) { + $sameSite = null; + } elseif (null !== $sameSite) { $sameSite = strtolower($sameSite); } - if (!\in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) { + if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) { throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.'); } @@ -142,7 +146,13 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom */ public function __toString() { - $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'='; + if ($this->isRaw()) { + $str = $this->getName(); + } else { + $str = str_replace(self::$reservedCharsFrom, self::$reservedCharsTo, $this->getName()); + } + + $str .= '='; if ('' === (string) $this->getValue()) { $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0'; @@ -246,7 +256,7 @@ public function getPath() */ public function isSecure() { - return $this->secure; + return $this->secure ?? $this->secureDefault; } /** @@ -288,4 +298,12 @@ public function getSameSite() { return $this->sameSite; } + + /** + * @param bool $default The default value of the "secure" flag when it is set to null + */ + public function setSecureDefault(bool $default): void + { + $this->secureDefault = $default; + } } diff --git a/vendor/symfony/http-foundation/ExpressionRequestMatcher.php b/vendor/symfony/http-foundation/ExpressionRequestMatcher.php index e9c8441..26bed7d 100644 --- a/vendor/symfony/http-foundation/ExpressionRequestMatcher.php +++ b/vendor/symfony/http-foundation/ExpressionRequestMatcher.php @@ -35,13 +35,13 @@ public function matches(Request $request) throw new \LogicException('Unable to match the request as the expression language is not available.'); } - return $this->language->evaluate($this->expression, array( + return $this->language->evaluate($this->expression, [ 'request' => $request, 'method' => $request->getMethod(), 'path' => rawurldecode($request->getPathInfo()), 'host' => $request->getHost(), 'ip' => $request->getClientIp(), 'attributes' => $request->attributes->all(), - )) && parent::matches($request); + ]) && parent::matches($request); } } diff --git a/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php b/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php index 3b8e41d..136d2a9 100644 --- a/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php +++ b/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php @@ -18,10 +18,7 @@ */ class AccessDeniedException extends FileException { - /** - * @param string $path The path to the accessed file - */ - public function __construct($path) + public function __construct(string $path) { parent::__construct(sprintf('The file %s could not be accessed', $path)); } diff --git a/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php b/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php new file mode 100644 index 0000000..c49f53a --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_CANT_WRITE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class CannotWriteFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php b/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php new file mode 100644 index 0000000..ed83499 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_EXTENSION error occurred with UploadedFile. + * + * @author Florent Mata + */ +class ExtensionFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php b/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php index bfcc37e..31bdf68 100644 --- a/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php +++ b/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php @@ -18,10 +18,7 @@ */ class FileNotFoundException extends FileException { - /** - * @param string $path The path to the file that was not found - */ - public function __construct($path) + public function __construct(string $path) { parent::__construct(sprintf('The file "%s" does not exist', $path)); } diff --git a/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php b/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php new file mode 100644 index 0000000..8741be0 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_FORM_SIZE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class FormSizeFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php b/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php new file mode 100644 index 0000000..c8fde61 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_INI_SIZE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class IniSizeFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/NoFileException.php b/vendor/symfony/http-foundation/File/Exception/NoFileException.php new file mode 100644 index 0000000..4b48cc7 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/NoFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_NO_FILE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class NoFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php b/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php new file mode 100644 index 0000000..bdead2d --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_NO_TMP_DIR error occurred with UploadedFile. + * + * @author Florent Mata + */ +class NoTmpDirFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/PartialFileException.php b/vendor/symfony/http-foundation/File/Exception/PartialFileException.php new file mode 100644 index 0000000..4641efb --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/PartialFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_PARTIAL error occurred with UploadedFile. + * + * @author Florent Mata + */ +class PartialFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php b/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php index 62005d3..82b982b 100644 --- a/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php +++ b/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php @@ -13,7 +13,7 @@ class UnexpectedTypeException extends FileException { - public function __construct($value, $expectedType) + public function __construct($value, string $expectedType) { parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, \is_object($value) ? \get_class($value) : \gettype($value))); } diff --git a/vendor/symfony/http-foundation/File/File.php b/vendor/symfony/http-foundation/File/File.php index 3422058..4906588 100644 --- a/vendor/symfony/http-foundation/File/File.php +++ b/vendor/symfony/http-foundation/File/File.php @@ -13,8 +13,7 @@ use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; -use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; -use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\Mime\MimeTypes; /** * A file in the file system. @@ -31,7 +30,7 @@ class File extends \SplFileInfo * * @throws FileNotFoundException If the given path is not a file */ - public function __construct($path, $checkPath = true) + public function __construct(string $path, bool $checkPath = true) { if ($checkPath && !is_file($path)) { throw new FileNotFoundException($path); @@ -50,33 +49,28 @@ public function __construct($path, $checkPath = true) * * @return string|null The guessed extension or null if it cannot be guessed * - * @see ExtensionGuesser + * @see MimeTypes * @see getMimeType() */ public function guessExtension() { - $type = $this->getMimeType(); - $guesser = ExtensionGuesser::getInstance(); - - return $guesser->guess($type); + return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null; } /** * Returns the mime type of the file. * - * The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(), - * mime_content_type() and the system binary "file" (in this order), depending on - * which of those are available. + * The mime type is guessed using a MimeTypeGuesserInterface instance, + * which uses finfo_file() then the "file" system binary, + * depending on which of those are available. * * @return string|null The guessed mime type (e.g. "application/pdf") * - * @see MimeTypeGuesser + * @see MimeTypes */ public function getMimeType() { - $guesser = MimeTypeGuesser::getInstance(); - - return $guesser->guess($this->getPathname()); + return MimeTypes::getDefault()->guessMimeType($this->getPathname()); } /** @@ -105,6 +99,9 @@ public function move($directory, $name = null) return $target; } + /** + * @return self + */ protected function getTargetFile($directory, $name = null) { if (!is_dir($directory)) { @@ -125,7 +122,7 @@ protected function getTargetFile($directory, $name = null) * * @param string $name The new file name * - * @return string containing + * @return string */ protected function getName($name) { diff --git a/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php index 263fb32..4ac2013 100644 --- a/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php +++ b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php @@ -11,6 +11,10 @@ namespace Symfony\Component\HttpFoundation\File\MimeType; +use Symfony\Component\Mime\MimeTypes; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', ExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED); + /** * A singleton mime type to file extension guesser. * @@ -22,6 +26,8 @@ * $guesser->register(new MyCustomExtensionGuesser()); * * The last registered guesser is preferred over previously registered ones. + * + * @deprecated since Symfony 4.3, use {@link MimeTypes} instead */ class ExtensionGuesser implements ExtensionGuesserInterface { @@ -37,7 +43,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface * * @var array */ - protected $guessers = array(); + protected $guessers = []; /** * Returns the singleton instance. @@ -90,5 +96,7 @@ public function guess($mimeType) return $extension; } } + + return null; } } diff --git a/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php index d19a0e5..69fe6ef 100644 --- a/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php +++ b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php @@ -11,8 +11,12 @@ namespace Symfony\Component\HttpFoundation\File\MimeType; +use Symfony\Component\Mime\MimeTypesInterface; + /** * Guesses the file extension corresponding to a given mime type. + * + * @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead */ interface ExtensionGuesserInterface { diff --git a/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php index 34e015e..5d3ae10 100644 --- a/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php +++ b/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -13,11 +13,16 @@ use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\Mime\FileBinaryMimeTypeGuesser as NewFileBinaryMimeTypeGuesser; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileBinaryMimeTypeGuesser::class, NewFileBinaryMimeTypeGuesser::class), E_USER_DEPRECATED); /** * Guesses the mime type with the binary "file" (only available on *nix). * * @author Bernhard Schussek + * + * @deprecated since Symfony 4.3, use {@link NewFileBinaryMimeTypeGuesser} instead */ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface { @@ -31,7 +36,7 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface * * @param string $cmd The command to run to get the mime type of a file */ - public function __construct($cmd = 'file -b --mime %s 2>/dev/null') + public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null') { $this->cmd = $cmd; } @@ -74,24 +79,24 @@ public function guess($path) } if (!self::isSupported()) { - return; + return null; } ob_start(); // need to use --mime instead of -i. see #6641 - passthru(sprintf($this->cmd, escapeshellarg($path)), $return); + passthru(sprintf($this->cmd, escapeshellarg((0 === strpos($path, '-') ? './' : '').$path)), $return); if ($return > 0) { ob_end_clean(); - return; + return null; } $type = trim(ob_get_clean()); - if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) { + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) { // it's not a type, but an error message - return; + return null; } return $match[1]; diff --git a/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php index bf1ee9f..70a01d7 100644 --- a/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php +++ b/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php @@ -13,11 +13,16 @@ use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\Mime\FileinfoMimeTypeGuesser as NewFileinfoMimeTypeGuesser; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileinfoMimeTypeGuesser::class, NewFileinfoMimeTypeGuesser::class), E_USER_DEPRECATED); /** * Guesses the mime type using the PECL extension FileInfo. * * @author Bernhard Schussek + * + * @deprecated since Symfony 4.3, use {@link NewFileinfoMimeTypeGuesser} instead */ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface { @@ -26,9 +31,9 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface /** * @param string $magicFile A magic file to use with the finfo instance * - * @see http://www.php.net/manual/en/function.finfo-open.php + * @see https://php.net/finfo-open */ - public function __construct($magicFile = null) + public function __construct(string $magicFile = null) { $this->magicFile = $magicFile; } @@ -57,11 +62,11 @@ public function guess($path) } if (!self::isSupported()) { - return; + return null; } if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) { - return; + return null; } return $finfo->file($path); diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php index 77bf51b..651be07 100644 --- a/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php +++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -11,8 +11,14 @@ namespace Symfony\Component\HttpFoundation\File\MimeType; +use Symfony\Component\Mime\MimeTypes; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED); + /** * Provides a best-guess mapping of mime type to file extension. + * + * @deprecated since Symfony 4.3, use {@link MimeTypes} instead */ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface { @@ -20,11 +26,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface * A map of mime types and their default extensions. * * This list has been placed under the public domain by the Apache HTTPD project. - * This list has been updated from upstream on 2013-04-23. + * This list has been updated from upstream on 2019-01-14. * - * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + * @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types */ - protected $defaultExtensions = array( + protected $defaultExtensions = [ 'application/andrew-inset' => 'ez', 'application/applixware' => 'aw', 'application/atom+xml' => 'atom', @@ -618,7 +624,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'audio/adpcm' => 'adp', 'audio/basic' => 'au', 'audio/midi' => 'mid', - 'audio/mp4' => 'mp4a', + 'audio/mp4' => 'm4a', 'audio/mpeg' => 'mpga', 'audio/ogg' => 'oga', 'audio/s3m' => 's3m', @@ -639,6 +645,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'audio/x-aiff' => 'aif', 'audio/x-caf' => 'caf', 'audio/x-flac' => 'flac', + 'audio/x-hx-aac-adts' => 'aac', 'audio/x-matroska' => 'mka', 'audio/x-mpegurl' => 'm3u', 'audio/x-ms-wax' => 'wax', @@ -653,6 +660,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'chemical/x-cml' => 'cml', 'chemical/x-csml' => 'csml', 'chemical/x-xyz' => 'xyz', + 'font/collection' => 'ttc', + 'font/otf' => 'otf', + 'font/ttf' => 'ttf', + 'font/woff' => 'woff', + 'font/woff2' => 'woff2', 'image/bmp' => 'bmp', 'image/x-ms-bmp' => 'bmp', 'image/cgm' => 'cgm', @@ -669,8 +681,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'image/tiff' => 'tiff', 'image/vnd.adobe.photoshop' => 'psd', 'image/vnd.dece.graphic' => 'uvi', - 'image/vnd.dvb.subtitle' => 'sub', 'image/vnd.djvu' => 'djvu', + 'image/vnd.dvb.subtitle' => 'sub', 'image/vnd.dwg' => 'dwg', 'image/vnd.dxf' => 'dxf', 'image/vnd.fastbidsheet' => 'fbs', @@ -732,8 +744,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'text/vcard' => 'vcard', 'text/vnd.curl' => 'curl', 'text/vnd.curl.dcurl' => 'dcurl', - 'text/vnd.curl.scurl' => 'scurl', 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.curl.scurl' => 'scurl', 'text/vnd.dvb.subtitle' => 'sub', 'text/vnd.fly' => 'fly', 'text/vnd.fmi.flexstor' => 'flx', @@ -747,10 +759,10 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'text/x-asm' => 's', 'text/x-c' => 'c', 'text/x-fortran' => 'f', - 'text/x-pascal' => 'p', 'text/x-java-source' => 'java', - 'text/x-opml' => 'opml', 'text/x-nfo' => 'nfo', + 'text/x-opml' => 'opml', + 'text/x-pascal' => 'p', 'text/x-setext' => 'etx', 'text/x-sfv' => 'sfv', 'text/x-uuencode' => 'uu', @@ -796,13 +808,19 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface 'video/x-sgi-movie' => 'movie', 'video/x-smv' => 'smv', 'x-conference/x-cooltalk' => 'ice', - ); + ]; /** * {@inheritdoc} */ public function guess($mimeType) { - return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null; + if (isset($this->defaultExtensions[$mimeType])) { + return $this->defaultExtensions[$mimeType]; + } + + $lcMimeType = strtolower($mimeType); + + return isset($this->defaultExtensions[$lcMimeType]) ? $this->defaultExtensions[$lcMimeType] : null; } } diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php index dae47a7..ece2109 100644 --- a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php +++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php @@ -13,6 +13,9 @@ use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\Mime\MimeTypes; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeGuesser::class, MimeTypes::class), E_USER_DEPRECATED); /** * A singleton mime type guesser. @@ -51,7 +54,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface * * @var array */ - protected $guessers = array(); + protected $guessers = []; /** * Returns the singleton instance. @@ -129,5 +132,7 @@ public function guess($path) if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) { throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)'); } + + return null; } } diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php index 5ac1acb..eab4448 100644 --- a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php +++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php @@ -13,11 +13,14 @@ use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\Mime\MimeTypesInterface; /** * Guesses the mime type of a file. * * @author Bernhard Schussek + * + * @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead */ interface MimeTypeGuesserInterface { @@ -26,7 +29,7 @@ interface MimeTypeGuesserInterface * * @param string $path The path to the file * - * @return string The mime type or NULL, if none could be guessed + * @return string|null The mime type or NULL, if none could be guessed * * @throws FileNotFoundException If the file does not exist * @throws AccessDeniedException If the file could not be read diff --git a/vendor/symfony/http-foundation/File/UploadedFile.php b/vendor/symfony/http-foundation/File/UploadedFile.php index de6ce75..0c67f89 100644 --- a/vendor/symfony/http-foundation/File/UploadedFile.php +++ b/vendor/symfony/http-foundation/File/UploadedFile.php @@ -11,9 +11,16 @@ namespace Symfony\Component\HttpFoundation\File; +use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException; +use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException; use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; -use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; +use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException; +use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException; +use Symfony\Component\HttpFoundation\File\Exception\NoFileException; +use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException; +use Symfony\Component\HttpFoundation\File\Exception\PartialFileException; +use Symfony\Component\Mime\MimeTypes; /** * A file uploaded through a form. @@ -27,7 +34,6 @@ class UploadedFile extends File private $test = false; private $originalName; private $mimeType; - private $size; private $error; /** @@ -47,7 +53,6 @@ class UploadedFile extends File * @param string $path The full temporary path to the file * @param string $originalName The original file name of the uploaded file * @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream - * @param int|null $size The file size provided by the uploader * @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK * @param bool $test Whether the test mode is active * Local files are used in test mode hence the code should not enforce HTTP uploads @@ -55,13 +60,19 @@ class UploadedFile extends File * @throws FileException If file_uploads is disabled * @throws FileNotFoundException If the file does not exist */ - public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) + public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false) { $this->originalName = $this->getName($originalName); $this->mimeType = $mimeType ?: 'application/octet-stream'; - $this->size = $size; + + if (4 < \func_num_args() ? !\is_bool($test) : null !== $error && @filesize($path) === $error) { + @trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED); + $error = $test; + $test = 5 < \func_num_args() ? func_get_arg(5) : false; + } + $this->error = $error ?: UPLOAD_ERR_OK; - $this->test = (bool) $test; + $this->test = $test; parent::__construct($path, UPLOAD_ERR_OK === $this->error); } @@ -129,10 +140,7 @@ public function getClientMimeType() */ public function guessClientExtension() { - $type = $this->getClientMimeType(); - $guesser = ExtensionGuesser::getInstance(); - - return $guesser->guess($type); + return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null; } /** @@ -141,11 +149,15 @@ public function guessClientExtension() * It is extracted from the request from which the file has been uploaded. * Then it should not be considered as a safe value. * - * @return int|null The file size + * @deprecated since Symfony 4.1, use getSize() instead. + * + * @return int|null The file sizes */ public function getClientSize() { - return $this->size; + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->getSize(); } /** @@ -204,6 +216,23 @@ public function move($directory, $name = null) return $target; } + switch ($this->error) { + case UPLOAD_ERR_INI_SIZE: + throw new IniSizeFileException($this->getErrorMessage()); + case UPLOAD_ERR_FORM_SIZE: + throw new FormSizeFileException($this->getErrorMessage()); + case UPLOAD_ERR_PARTIAL: + throw new PartialFileException($this->getErrorMessage()); + case UPLOAD_ERR_NO_FILE: + throw new NoFileException($this->getErrorMessage()); + case UPLOAD_ERR_CANT_WRITE: + throw new CannotWriteFileException($this->getErrorMessage()); + case UPLOAD_ERR_NO_TMP_DIR: + throw new NoTmpDirFileException($this->getErrorMessage()); + case UPLOAD_ERR_EXTENSION: + throw new ExtensionFileException($this->getErrorMessage()); + } + throw new FileException($this->getErrorMessage()); } @@ -214,13 +243,24 @@ public function move($directory, $name = null) */ public static function getMaxFilesize() { - $iniMax = strtolower(ini_get('upload_max_filesize')); + $sizePostMax = self::parseFilesize(ini_get('post_max_size')); + $sizeUploadMax = self::parseFilesize(ini_get('upload_max_filesize')); + + return min($sizePostMax ?: PHP_INT_MAX, $sizeUploadMax ?: PHP_INT_MAX); + } - if ('' === $iniMax) { - return PHP_INT_MAX; + /** + * Returns the given size from an ini value in bytes. + */ + private static function parseFilesize($size): int + { + if ('' === $size) { + return 0; } - $max = ltrim($iniMax, '+'); + $size = strtolower($size); + + $max = ltrim($size, '+'); if (0 === strpos($max, '0x')) { $max = \intval($max, 16); } elseif (0 === strpos($max, '0')) { @@ -229,7 +269,7 @@ public static function getMaxFilesize() $max = (int) $max; } - switch (substr($iniMax, -1)) { + switch (substr($size, -1)) { case 't': $max *= 1024; // no break case 'g': $max *= 1024; @@ -249,7 +289,7 @@ public static function getMaxFilesize() */ public function getErrorMessage() { - static $errors = array( + static $errors = [ UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).', UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.', UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.', @@ -257,7 +297,7 @@ public function getErrorMessage() UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.', UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.', UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.', - ); + ]; $errorCode = $this->error; $maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0; diff --git a/vendor/symfony/http-foundation/FileBag.php b/vendor/symfony/http-foundation/FileBag.php index c135ad6..d79075c 100644 --- a/vendor/symfony/http-foundation/FileBag.php +++ b/vendor/symfony/http-foundation/FileBag.php @@ -21,12 +21,12 @@ */ class FileBag extends ParameterBag { - private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + private static $fileKeys = ['error', 'name', 'size', 'tmp_name', 'type']; /** - * @param array $parameters An array of HTTP files + * @param array|UploadedFile[] $parameters An array of HTTP files */ - public function __construct(array $parameters = array()) + public function __construct(array $parameters = []) { $this->replace($parameters); } @@ -34,9 +34,9 @@ public function __construct(array $parameters = array()) /** * {@inheritdoc} */ - public function replace(array $files = array()) + public function replace(array $files = []) { - $this->parameters = array(); + $this->parameters = []; $this->add($files); } @@ -55,7 +55,7 @@ public function set($key, $value) /** * {@inheritdoc} */ - public function add(array $files = array()) + public function add(array $files = []) { foreach ($files as $key => $file) { $this->set($key, $file); @@ -75,8 +75,8 @@ protected function convertFileInformation($file) return $file; } - $file = $this->fixPhpFilesArray($file); if (\is_array($file)) { + $file = $this->fixPhpFilesArray($file); $keys = array_keys($file); sort($keys); @@ -84,10 +84,10 @@ protected function convertFileInformation($file) if (UPLOAD_ERR_NO_FILE == $file['error']) { $file = null; } else { - $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false); } } else { - $file = array_map(array($this, 'convertFileInformation'), $file); + $file = array_map([$this, 'convertFileInformation'], $file); if (array_keys($keys) === $keys) { $file = array_filter($file); } @@ -109,14 +109,12 @@ protected function convertFileInformation($file) * It's safe to pass an already converted array, in which case this method * just returns the original array unmodified. * + * @param array $data + * * @return array */ protected function fixPhpFilesArray($data) { - if (!\is_array($data)) { - return $data; - } - $keys = array_keys($data); sort($keys); @@ -130,13 +128,13 @@ protected function fixPhpFilesArray($data) } foreach ($data['name'] as $key => $name) { - $files[$key] = $this->fixPhpFilesArray(array( + $files[$key] = $this->fixPhpFilesArray([ 'error' => $data['error'][$key], 'name' => $name, 'type' => $data['type'][$key], 'tmp_name' => $data['tmp_name'][$key], 'size' => $data['size'][$key], - )); + ]); } return $files; diff --git a/vendor/symfony/http-foundation/HeaderBag.php b/vendor/symfony/http-foundation/HeaderBag.php index 2302536..9ffe6f4 100644 --- a/vendor/symfony/http-foundation/HeaderBag.php +++ b/vendor/symfony/http-foundation/HeaderBag.php @@ -18,13 +18,13 @@ */ class HeaderBag implements \IteratorAggregate, \Countable { - protected $headers = array(); - protected $cacheControl = array(); + protected const UPPER = '_ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + protected const LOWER = '-abcdefghijklmnopqrstuvwxyz'; - /** - * @param array $headers An array of HTTP headers - */ - public function __construct(array $headers = array()) + protected $headers = []; + protected $cacheControl = []; + + public function __construct(array $headers = []) { foreach ($headers as $key => $values) { $this->set($key, $values); @@ -46,7 +46,7 @@ public function __toString() $max = max(array_map('strlen', array_keys($headers))) + 1; $content = ''; foreach ($headers as $name => $values) { - $name = implode('-', array_map('ucfirst', explode('-', $name))); + $name = ucwords($name, '-'); foreach ($values as $value) { $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value); } @@ -58,10 +58,16 @@ public function __toString() /** * Returns the headers. * + * @param string|null $key The name of the headers to return or null to get them all + * * @return array An array of headers */ - public function all() + public function all(/*string $key = null*/) { + if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) { + return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? []; + } + return $this->headers; } @@ -77,19 +83,15 @@ public function keys() /** * Replaces the current HTTP headers by a new set. - * - * @param array $headers An array of HTTP headers */ - public function replace(array $headers = array()) + public function replace(array $headers = []) { - $this->headers = array(); + $this->headers = []; $this->add($headers); } /** * Adds new headers the current HTTP headers set. - * - * @param array $headers An array of HTTP headers */ public function add(array $headers) { @@ -103,28 +105,29 @@ public function add(array $headers) * * @param string $key The header name * @param string|null $default The default value - * @param bool $first Whether to return the first value or all header values * - * @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise + * @return string|null The first header value or default value */ - public function get($key, $default = null, $first = true) + public function get($key, $default = null) { - $key = str_replace('_', '-', strtolower($key)); - $headers = $this->all(); + $headers = $this->all((string) $key); + if (2 < \func_num_args()) { + @trigger_error(sprintf('Passing a third argument to "%s()" is deprecated since Symfony 4.4, use method "all()" instead', __METHOD__), E_USER_DEPRECATED); - if (!array_key_exists($key, $headers)) { - if (null === $default) { - return $first ? null : array(); + if (!func_get_arg(2)) { + return $headers; } + } - return $first ? $default : array($default); + if (!$headers) { + return $default; } - if ($first) { - return \count($headers[$key]) ? $headers[$key][0] : $default; + if (null === $headers[0]) { + return null; } - return $headers[$key]; + return (string) $headers[0]; } /** @@ -136,7 +139,7 @@ public function get($key, $default = null, $first = true) */ public function set($key, $values, $replace = true) { - $key = str_replace('_', '-', strtolower($key)); + $key = strtr($key, self::UPPER, self::LOWER); if (\is_array($values)) { $values = array_values($values); @@ -148,7 +151,7 @@ public function set($key, $values, $replace = true) } } else { if (true === $replace || !isset($this->headers[$key])) { - $this->headers[$key] = array($values); + $this->headers[$key] = [$values]; } else { $this->headers[$key][] = $values; } @@ -168,7 +171,7 @@ public function set($key, $values, $replace = true) */ public function has($key) { - return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all()); + return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all()); } /** @@ -181,7 +184,7 @@ public function has($key) */ public function contains($key, $value) { - return \in_array($value, $this->get($key, null, false)); + return \in_array($value, $this->all((string) $key)); } /** @@ -191,22 +194,21 @@ public function contains($key, $value) */ public function remove($key) { - $key = str_replace('_', '-', strtolower($key)); + $key = strtr($key, self::UPPER, self::LOWER); unset($this->headers[$key]); if ('cache-control' === $key) { - $this->cacheControl = array(); + $this->cacheControl = []; } } /** * Returns the HTTP header value converted to a date. * - * @param string $key The parameter key - * @param \DateTime $default The default value + * @param string $key The parameter key * - * @return \DateTime|null The parsed DateTime or the default value if the header does not exist + * @return \DateTimeInterface|null The parsed DateTime or the default value if the header does not exist * * @throws \RuntimeException When the HTTP header is not parseable */ @@ -245,7 +247,7 @@ public function addCacheControlDirective($key, $value = true) */ public function hasCacheControlDirective($key) { - return array_key_exists($key, $this->cacheControl); + return \array_key_exists($key, $this->cacheControl); } /** @@ -257,7 +259,7 @@ public function hasCacheControlDirective($key) */ public function getCacheControlDirective($key) { - return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; + return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; } /** @@ -294,21 +296,9 @@ public function count() protected function getCacheControlHeader() { - $parts = array(); ksort($this->cacheControl); - foreach ($this->cacheControl as $key => $value) { - if (true === $value) { - $parts[] = $key; - } else { - if (preg_match('#[^a-zA-Z0-9._-]#', $value)) { - $value = '"'.$value.'"'; - } - $parts[] = "$key=$value"; - } - } - - return implode(', ', $parts); + return HeaderUtils::toString($this->cacheControl, ','); } /** @@ -320,12 +310,8 @@ protected function getCacheControlHeader() */ protected function parseCacheControl($header) { - $cacheControl = array(); - preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true); - } + $parts = HeaderUtils::split($header, ',='); - return $cacheControl; + return HeaderUtils::combine($parts); } } diff --git a/vendor/symfony/http-foundation/HeaderUtils.php b/vendor/symfony/http-foundation/HeaderUtils.php new file mode 100644 index 0000000..5866e3b --- /dev/null +++ b/vendor/symfony/http-foundation/HeaderUtils.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * HTTP header utility functions. + * + * @author Christian Schmidt + */ +class HeaderUtils +{ + public const DISPOSITION_ATTACHMENT = 'attachment'; + public const DISPOSITION_INLINE = 'inline'; + + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Splits an HTTP header by one or more separators. + * + * Example: + * + * 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 ",;=" + * + * @return array Nested array with as many levels as there are characters in + * $separators + */ + public static function split(string $header, string $separators): array + { + $quotedSeparators = preg_quote($separators, '/'); + + preg_match_all(' + / + (?!\s) + (?: + # quoted-string + "(?:[^"\\\\]|\\\\.)*(?:"|\\\\|$) + | + # token + [^"'.$quotedSeparators.']+ + )+ + (?['.$quotedSeparators.']) + \s* + /x', trim($header), $matches, PREG_SET_ORDER); + + return self::groupParts($matches, $separators); + } + + /** + * Combines an array of arrays into one associative array. + * + * Each of the nested arrays should have one or two elements. The first + * value will be used as the keys in the associative array, and the second + * will be used as the values, or true if the nested array only contains one + * element. Array keys are lowercased. + * + * Example: + * + * HeaderUtils::combine([["foo", "abc"], ["bar"]]) + * // => ["foo" => "abc", "bar" => true] + */ + public static function combine(array $parts): array + { + $assoc = []; + foreach ($parts as $part) { + $name = strtolower($part[0]); + $value = $part[1] ?? true; + $assoc[$name] = $value; + } + + return $assoc; + } + + /** + * 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 + * 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"], ",") + * // => 'foo=abc, bar, baz="a b c"' + */ + public static function toString(array $assoc, string $separator): string + { + $parts = []; + foreach ($assoc as $name => $value) { + if (true === $value) { + $parts[] = $name; + } else { + $parts[] = $name.'='.self::quote($value); + } + } + + return implode($separator.' ', $parts); + } + + /** + * Encodes a string as a quoted string, if necessary. + * + * If a string contains characters not allowed by the "token" construct in + * the HTTP specification, it is backslash-escaped and enclosed in quotes + * to match the "quoted-string" construct. + */ + public static function quote(string $s): string + { + if (preg_match('/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s)) { + return $s; + } + + return '"'.addcslashes($s, '"\\"').'"'; + } + + /** + * Decodes a quoted string. + * + * If passed an unquoted string that matches the "token" construct (as + * defined in the HTTP specification), it is passed through verbatimly. + */ + public static function unquote(string $s): string + { + return preg_replace('/\\\\(.)|"/', '$1', $s); + } + + /** + * Generates a HTTP Content-Disposition field-value. + * + * @param string $disposition One of "inline" or "attachment" + * @param string $filename A unicode string + * @param string $filenameFallback A string containing only ASCII characters that + * is semantically equivalent to $filename. If the filename is already ASCII, + * it can be omitted, or just copied from $filename + * + * @return string A string suitable for use as a Content-Disposition field-value + * + * @throws \InvalidArgumentException + * + * @see RFC 6266 + */ + public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string + { + if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) { + throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); + } + + if ('' === $filenameFallback) { + $filenameFallback = $filename; + } + + // filenameFallback is not ASCII. + if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) { + throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); + } + + // percent characters aren't safe in fallback. + if (false !== strpos($filenameFallback, '%')) { + throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); + } + + // path separators aren't allowed in either. + if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) { + throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); + } + + $params = ['filename' => $filenameFallback]; + if ($filename !== $filenameFallback) { + $params['filename*'] = "utf-8''".rawurlencode($filename); + } + + return $disposition.'; '.self::toString($params, ';'); + } + + private static function groupParts(array $matches, string $separators): array + { + $separator = $separators[0]; + $partSeparators = substr($separators, 1); + + $i = 0; + $partMatches = []; + foreach ($matches as $match) { + if (isset($match['separator']) && $match['separator'] === $separator) { + ++$i; + } else { + $partMatches[$i][] = $match; + } + } + + $parts = []; + if ($partSeparators) { + foreach ($partMatches as $matches) { + $parts[] = self::groupParts($matches, $partSeparators); + } + } else { + foreach ($partMatches as $matches) { + $parts[] = self::unquote($matches[0][0]); + } + } + + return $parts; + } +} diff --git a/vendor/symfony/http-foundation/IpUtils.php b/vendor/symfony/http-foundation/IpUtils.php index a1bfa90..72c53a4 100644 --- a/vendor/symfony/http-foundation/IpUtils.php +++ b/vendor/symfony/http-foundation/IpUtils.php @@ -18,7 +18,7 @@ */ class IpUtils { - private static $checkedIps = array(); + private static $checkedIps = []; /** * This class should not be instantiated. @@ -38,7 +38,7 @@ private function __construct() public static function checkIp($requestIp, $ips) { if (!\is_array($ips)) { - $ips = array($ips); + $ips = [$ips]; } $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4'; @@ -153,4 +153,36 @@ public static function checkIp6($requestIp, $ip) return self::$checkedIps[$cacheKey] = true; } + + /** + * Anonymizes an IP/IPv6. + * + * Removes the last byte for v4 and the last 8 bytes for v6 IPs + */ + public static function anonymize(string $ip): string + { + $wrappedIPv6 = false; + if ('[' === substr($ip, 0, 1) && ']' === substr($ip, -1, 1)) { + $wrappedIPv6 = true; + $ip = substr($ip, 1, -1); + } + + $packedAddress = inet_pton($ip); + if (4 === \strlen($packedAddress)) { + $mask = '255.255.255.0'; + } elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff:ffff'))) { + $mask = '::ffff:ffff:ff00'; + } elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff'))) { + $mask = '::ffff:ff00'; + } else { + $mask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; + } + $ip = inet_ntop($packedAddress & inet_pton($mask)); + + if ($wrappedIPv6) { + $ip = '['.$ip.']'; + } + + return $ip; + } } diff --git a/vendor/symfony/http-foundation/JsonResponse.php b/vendor/symfony/http-foundation/JsonResponse.php index d741ce0..11a0beb 100644 --- a/vendor/symfony/http-foundation/JsonResponse.php +++ b/vendor/symfony/http-foundation/JsonResponse.php @@ -18,7 +18,7 @@ * object. It is however recommended that you do return an object as it * protects yourself against XSSI and JSON-JavaScript Hijacking. * - * @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside + * @see https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/AJAX_Security_Cheat_Sheet.md#always-return-json-with-an-object-on-the-outside * * @author Igor Wiedler */ @@ -39,7 +39,7 @@ class JsonResponse extends Response * @param array $headers An array of response headers * @param bool $json If the data is already a JSON string */ - public function __construct($data = null, $status = 200, $headers = array(), $json = false) + public function __construct($data = null, int $status = 200, array $headers = [], bool $json = false) { parent::__construct('', $status, $headers); @@ -55,24 +55,35 @@ public function __construct($data = null, $status = 200, $headers = array(), $js * * Example: * - * return JsonResponse::create($data, 200) + * return JsonResponse::create(['key' => 'value']) * ->setSharedMaxAge(300); * - * @param mixed $data The json response data + * @param mixed $data The JSON response data * @param int $status The response status code * @param array $headers An array of response headers * * @return static */ - public static function create($data = null, $status = 200, $headers = array()) + public static function create($data = null, $status = 200, $headers = []) { return new static($data, $status, $headers); } /** - * Make easier the creation of JsonResponse from raw json. + * Factory method for chainability. + * + * Example: + * + * return JsonResponse::fromJsonString('{"key": "value"}') + * ->setSharedMaxAge(300); + * + * @param string|null $data The JSON response string + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @return static */ - public static function fromJsonString($data = null, $status = 200, $headers = array()) + public static function fromJsonString($data = null, $status = 200, $headers = []) { return new static($data, $status, $headers, true); } @@ -89,16 +100,16 @@ public static function fromJsonString($data = null, $status = 200, $headers = ar public function setCallback($callback = null) { if (null !== $callback) { - // partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/ + // partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/ // partially taken from https://github.com/willdurand/JsonpCallbackValidator // JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details. // (c) William Durand $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u'; - $reserved = array( + $reserved = [ 'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while', 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export', 'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false', - ); + ]; $parts = explode('.', $callback); foreach ($parts as $part) { if (!preg_match($pattern, $part) || \in_array($part, $reserved, true)) { @@ -137,31 +148,19 @@ public function setJson($json) * * @throws \InvalidArgumentException */ - public function setData($data = array()) + public function setData($data = []) { - if (\defined('HHVM_VERSION')) { - // HHVM does not trigger any warnings and let exceptions - // thrown from a JsonSerializable object pass through. - // If only PHP did the same... + try { $data = json_encode($data, $this->encodingOptions); - } else { - if (!interface_exists('JsonSerializable', false)) { - set_error_handler(function () { return false; }); - try { - $data = @json_encode($data, $this->encodingOptions); - } finally { - restore_error_handler(); - } - } else { - try { - $data = json_encode($data, $this->encodingOptions); - } catch (\Exception $e) { - if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) { - throw $e->getPrevious() ?: $e; - } - throw $e; - } + } catch (\Exception $e) { + if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) { + throw $e->getPrevious() ?: $e; } + throw $e; + } + + if (\PHP_VERSION_ID >= 70300 && (JSON_THROW_ON_ERROR & $this->encodingOptions)) { + return $this->setJson($data); } if (JSON_ERROR_NONE !== json_last_error()) { diff --git a/vendor/symfony/http-foundation/LICENSE b/vendor/symfony/http-foundation/LICENSE index a677f43..9e936ec 100644 --- a/vendor/symfony/http-foundation/LICENSE +++ b/vendor/symfony/http-foundation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2019 Fabien Potencier +Copyright (c) 2004-2020 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/symfony/http-foundation/ParameterBag.php b/vendor/symfony/http-foundation/ParameterBag.php index 19d7ee9..20ca675 100644 --- a/vendor/symfony/http-foundation/ParameterBag.php +++ b/vendor/symfony/http-foundation/ParameterBag.php @@ -23,10 +23,7 @@ class ParameterBag implements \IteratorAggregate, \Countable */ protected $parameters; - /** - * @param array $parameters An array of parameters - */ - public function __construct(array $parameters = array()) + public function __construct(array $parameters = []) { $this->parameters = $parameters; } @@ -53,20 +50,16 @@ public function keys() /** * Replaces the current parameters by a new set. - * - * @param array $parameters An array of parameters */ - public function replace(array $parameters = array()) + public function replace(array $parameters = []) { $this->parameters = $parameters; } /** * Adds parameters. - * - * @param array $parameters An array of parameters */ - public function add(array $parameters = array()) + public function add(array $parameters = []) { $this->parameters = array_replace($this->parameters, $parameters); } @@ -81,7 +74,7 @@ public function add(array $parameters = array()) */ public function get($key, $default = null) { - return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; + return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; } /** @@ -104,7 +97,7 @@ public function set($key, $value) */ public function has($key) { - return array_key_exists($key, $this->parameters); + return \array_key_exists($key, $this->parameters); } /** @@ -154,7 +147,7 @@ public function getAlnum($key, $default = '') public function getDigits($key, $default = '') { // we need to remove - and + because they're allowed in the filter - return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT)); + return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT)); } /** @@ -191,17 +184,17 @@ public function getBoolean($key, $default = false) * @param int $filter FILTER_* constant * @param mixed $options Filter options * - * @see http://php.net/manual/en/function.filter-var.php + * @see https://php.net/filter-var * * @return mixed */ - public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array()) + public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = []) { $value = $this->get($key, $default); // Always turn $options into an array - this allows filter_var option shortcuts. if (!\is_array($options) && $options) { - $options = array('flags' => $options); + $options = ['flags' => $options]; } // Add a convenience check for arrays. diff --git a/vendor/symfony/http-foundation/README.md b/vendor/symfony/http-foundation/README.md index 8907f0b..ac98f9b 100644 --- a/vendor/symfony/http-foundation/README.md +++ b/vendor/symfony/http-foundation/README.md @@ -7,7 +7,7 @@ specification. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/http_foundation/index.html) + * [Documentation](https://symfony.com/doc/current/components/http_foundation.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) diff --git a/vendor/symfony/http-foundation/RedirectResponse.php b/vendor/symfony/http-foundation/RedirectResponse.php index 01681dc..4347f3a 100644 --- a/vendor/symfony/http-foundation/RedirectResponse.php +++ b/vendor/symfony/http-foundation/RedirectResponse.php @@ -30,10 +30,15 @@ class RedirectResponse extends Response * * @throws \InvalidArgumentException * - * @see http://tools.ietf.org/html/rfc2616#section-10.3 + * @see https://tools.ietf.org/html/rfc2616#section-10.3 */ - public function __construct($url, $status = 302, $headers = array()) + public function __construct(?string $url, int $status = 302, array $headers = []) { + if (null === $url) { + @trigger_error(sprintf('Passing a null url when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED); + $url = ''; + } + parent::__construct('', $status, $headers); $this->setTargetUrl($url); @@ -42,7 +47,7 @@ public function __construct($url, $status = 302, $headers = array()) throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status)); } - if (301 == $status && !array_key_exists('cache-control', $headers)) { + if (301 == $status && !\array_key_exists('cache-control', array_change_key_case($headers, CASE_LOWER))) { $this->headers->remove('cache-control'); } } @@ -56,7 +61,7 @@ public function __construct($url, $status = 302, $headers = array()) * * @return static */ - public static function create($url = '', $status = 302, $headers = array()) + public static function create($url = '', $status = 302, $headers = []) { return new static($url, $status, $headers); } @@ -82,7 +87,7 @@ public function getTargetUrl() */ public function setTargetUrl($url) { - if (empty($url)) { + if ('' === ($url ?? '')) { throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); } @@ -93,7 +98,7 @@ public function setTargetUrl($url) - + Redirecting to %1$s diff --git a/vendor/symfony/http-foundation/Request.php b/vendor/symfony/http-foundation/Request.php index 10687e3..6690b9b 100644 --- a/vendor/symfony/http-foundation/Request.php +++ b/vendor/symfony/http-foundation/Request.php @@ -38,15 +38,6 @@ class Request const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR; - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST; - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO; - /** @deprecated since version 3.3, to be removed in 4.0 */ - const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT; - const METHOD_HEAD = 'HEAD'; const METHOD_GET = 'GET'; const METHOD_POST = 'POST'; @@ -61,85 +52,66 @@ class Request /** * @var string[] */ - protected static $trustedProxies = array(); + protected static $trustedProxies = []; /** * @var string[] */ - protected static $trustedHostPatterns = array(); + protected static $trustedHostPatterns = []; /** * @var string[] */ - protected static $trustedHosts = array(); - - /** - * Names for headers that can be trusted when - * using trusted proxies. - * - * The FORWARDED header is the standard as of rfc7239. - * - * The other headers are non-standard, but widely used - * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). - * - * @deprecated since version 3.3, to be removed in 4.0 - */ - protected static $trustedHeaders = array( - self::HEADER_FORWARDED => 'FORWARDED', - self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', - self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', - self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', - self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', - ); + protected static $trustedHosts = []; protected static $httpMethodParameterOverride = false; /** * Custom parameters. * - * @var \Symfony\Component\HttpFoundation\ParameterBag + * @var ParameterBag */ public $attributes; /** * Request body parameters ($_POST). * - * @var \Symfony\Component\HttpFoundation\ParameterBag + * @var ParameterBag */ public $request; /** * Query string parameters ($_GET). * - * @var \Symfony\Component\HttpFoundation\ParameterBag + * @var ParameterBag */ public $query; /** * Server and execution environment parameters ($_SERVER). * - * @var \Symfony\Component\HttpFoundation\ServerBag + * @var ServerBag */ public $server; /** * Uploaded files ($_FILES). * - * @var \Symfony\Component\HttpFoundation\FileBag + * @var FileBag */ public $files; /** * Cookies ($_COOKIE). * - * @var \Symfony\Component\HttpFoundation\ParameterBag + * @var ParameterBag */ public $cookies; /** * Headers (taken from the $_SERVER). * - * @var \Symfony\Component\HttpFoundation\HeaderBag + * @var HeaderBag */ public $headers; @@ -199,7 +171,7 @@ class Request protected $format; /** - * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + * @var SessionInterface */ protected $session; @@ -220,26 +192,38 @@ class Request protected static $requestFactory; + /** + * @var string|null + */ + private $preferredFormat; private $isHostValid = true; private $isForwardedValid = true; private static $trustedHeaderSet = -1; - /** @deprecated since version 3.3, to be removed in 4.0 */ - private static $trustedHeaderNames = array( - self::HEADER_FORWARDED => 'FORWARDED', - self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', - self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', - self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', - self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', - ); - - private static $forwardedParams = array( + private static $forwardedParams = [ self::HEADER_X_FORWARDED_FOR => 'for', self::HEADER_X_FORWARDED_HOST => 'host', self::HEADER_X_FORWARDED_PROTO => 'proto', self::HEADER_X_FORWARDED_PORT => 'host', - ); + ]; + + /** + * Names for headers that can be trusted when + * using trusted proxies. + * + * The FORWARDED header is the standard as of rfc7239. + * + * The other headers are non-standard, but widely used + * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). + */ + private static $trustedHeaders = [ + self::HEADER_FORWARDED => 'FORWARDED', + self::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR', + self::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST', + self::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO', + self::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT', + ]; /** * @param array $query The GET parameters @@ -250,7 +234,7 @@ class Request * @param array $server The SERVER parameters * @param string|resource|null $content The raw body data */ - public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null) { $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); } @@ -268,7 +252,7 @@ public function __construct(array $query = array(), array $request = array(), ar * @param array $server The SERVER parameters * @param string|resource|null $content The raw body data */ - public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null) { $this->request = new ParameterBag($request); $this->query = new ParameterBag($query); @@ -298,23 +282,10 @@ public function initialize(array $query = array(), array $request = array(), arr */ public static function createFromGlobals() { - // With the php's bug #66606, the php's built-in web server - // stores the Content-Type and Content-Length header values in - // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. - $server = $_SERVER; - if ('cli-server' === \PHP_SAPI) { - if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) { - $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH']; - } - if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { - $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE']; - } - } - - $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); + $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER); if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') - && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) + && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH']) ) { parse_str($request->getContent(), $data); $request->request = new ParameterBag($data); @@ -339,13 +310,13 @@ public static function createFromGlobals() * * @return static */ - public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) + public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { - $server = array_replace(array( + $server = array_replace([ 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => 80, 'HTTP_HOST' => 'localhost', - 'HTTP_USER_AGENT' => 'Symfony/3.X', + 'HTTP_USER_AGENT' => 'Symfony', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', @@ -354,7 +325,7 @@ public static function create($uri, $method = 'GET', $parameters = array(), $coo 'SCRIPT_FILENAME' => '', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'REQUEST_TIME' => time(), - ), $server); + ], $server); $server['PATH_INFO'] = ''; $server['REQUEST_METHOD'] = strtoupper($method); @@ -402,10 +373,10 @@ public static function create($uri, $method = 'GET', $parameters = array(), $coo // no break case 'PATCH': $request = $parameters; - $query = array(); + $query = []; break; default: - $request = array(); + $request = []; $query = $parameters; break; } @@ -428,7 +399,7 @@ public static function create($uri, $method = 'GET', $parameters = array(), $coo $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : ''); $server['QUERY_STRING'] = $queryString; - return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content); + return self::createRequestFromFactory($query, $request, [], $cookies, $files, $server, $content); } /** @@ -528,11 +499,15 @@ public function __toString() try { $content = $this->getContent(); } catch (\LogicException $e) { + if (\PHP_VERSION_ID >= 70400) { + throw $e; + } + return trigger_error($e, E_USER_ERROR); } $cookieHeader = ''; - $cookies = array(); + $cookies = []; foreach ($this->cookies as $k => $v) { $cookies[] = $k.'='.$v; @@ -566,22 +541,25 @@ public function overrideGlobals() foreach ($this->headers->all() as $key => $value) { $key = strtoupper(str_replace('-', '_', $key)); - if (\in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { + if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) { $_SERVER[$key] = implode(', ', $value); } else { $_SERVER['HTTP_'.$key] = implode(', ', $value); } } - $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE); + $request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE]; $requestOrder = ini_get('request_order') ?: ini_get('variables_order'); $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp'; - $_REQUEST = array(); + $_REQUEST = [[]]; + foreach (str_split($requestOrder) as $order) { - $_REQUEST = array_merge($_REQUEST, $request[$order]); + $_REQUEST[] = $request[$order]; } + + $_REQUEST = array_merge(...$_REQUEST); } /** @@ -589,25 +567,22 @@ public function overrideGlobals() * * You should only list the reverse proxies that you manage directly. * - * @param array $proxies A list of trusted proxies + * @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR'] * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies * * @throws \InvalidArgumentException When $trustedHeaderSet is invalid */ - public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/) + public static function setTrustedProxies(array $proxies, int $trustedHeaderSet) { - self::$trustedProxies = $proxies; - - if (2 > \func_num_args()) { - @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED); - - return; - } - $trustedHeaderSet = (int) func_get_arg(1); + self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) { + if ('REMOTE_ADDR' !== $proxy) { + $proxies[] = $proxy; + } elseif (isset($_SERVER['REMOTE_ADDR'])) { + $proxies[] = $_SERVER['REMOTE_ADDR']; + } - foreach (self::$trustedHeaderNames as $header => $name) { - self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null; - } + return $proxies; + }, []); self::$trustedHeaderSet = $trustedHeaderSet; } @@ -644,7 +619,7 @@ public static function setTrustedHosts(array $hostPatterns) return sprintf('{%s}i', $hostPattern); }, $hostPatterns); // we need to reset trusted hosts on trusted host patterns change - self::$trustedHosts = array(); + self::$trustedHosts = []; } /** @@ -657,78 +632,6 @@ public static function getTrustedHosts() return self::$trustedHostPatterns; } - /** - * Sets the name for trusted headers. - * - * The following header keys are supported: - * - * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp()) - * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost()) - * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort()) - * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure()) - * * Request::HEADER_FORWARDED: defaults to Forwarded (see RFC 7239) - * - * Setting an empty value allows to disable the trusted header for the given key. - * - * @param string $key The header key - * @param string $value The header name - * - * @throws \InvalidArgumentException - * - * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. - */ - public static function setTrustedHeaderName($key, $value) - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED); - - if ('forwarded' === $key) { - $key = self::HEADER_FORWARDED; - } elseif ('client_ip' === $key) { - $key = self::HEADER_CLIENT_IP; - } elseif ('client_host' === $key) { - $key = self::HEADER_CLIENT_HOST; - } elseif ('client_proto' === $key) { - $key = self::HEADER_CLIENT_PROTO; - } elseif ('client_port' === $key) { - $key = self::HEADER_CLIENT_PORT; - } elseif (!array_key_exists($key, self::$trustedHeaders)) { - throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); - } - - self::$trustedHeaders[$key] = $value; - - if (null !== $value) { - self::$trustedHeaderNames[$key] = $value; - self::$trustedHeaderSet |= $key; - } else { - self::$trustedHeaderSet &= ~$key; - } - } - - /** - * Gets the trusted proxy header name. - * - * @param string $key The header key - * - * @return string The header name - * - * @throws \InvalidArgumentException - * - * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead. - */ - public static function getTrustedHeaderName($key) - { - if (2 > \func_num_args() || func_get_arg(1)) { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED); - } - - if (!array_key_exists($key, self::$trustedHeaders)) { - throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key)); - } - - return self::$trustedHeaders[$key]; - } - /** * Normalizes a query string. * @@ -741,35 +644,14 @@ public static function getTrustedHeaderName($key) */ public static function normalizeQueryString($qs) { - if ('' == $qs) { + if ('' === ($qs ?? '')) { return ''; } - $parts = array(); - $order = array(); + parse_str($qs, $qs); + ksort($qs); - foreach (explode('&', $qs) as $param) { - if ('' === $param || '=' === $param[0]) { - // Ignore useless delimiters, e.g. "x=y&". - // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway. - // PHP also does not include them when building _GET. - continue; - } - - $keyValuePair = explode('=', $param, 2); - - // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded). - // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. This is why we use urldecode and then normalize to - // RFC 3986 with rawurlencode. - $parts[] = isset($keyValuePair[1]) ? - rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) : - rawurlencode(urldecode($keyValuePair[0])); - $order[] = urldecode($keyValuePair[0]); - } - - array_multisort($order, SORT_ASC, $parts); - - return implode('&', $parts); + return http_build_query($qs, '', '&', PHP_QUERY_RFC3986); } /** @@ -832,11 +714,21 @@ public function get($key, $default = null) /** * Gets the Session. * - * @return SessionInterface|null The session + * @return SessionInterface The session */ public function getSession() { - return $this->session; + $session = $this->session; + if (!$session instanceof SessionInterface && null !== $session) { + $this->setSession($session = $session()); + } + + if (null === $session) { + @trigger_error(sprintf('Calling "%s()" when no session has been set is deprecated since Symfony 4.1 and will throw an exception in 5.0. Use "hasSession()" instead.', __METHOD__), E_USER_DEPRECATED); + // throw new \BadMethodCallException('Session has not been set'); + } + + return $session; } /** @@ -848,7 +740,7 @@ public function getSession() public function hasPreviousSession() { // the check for $this->session avoids malicious users trying to fake a session cookie with proper name - return $this->hasSession() && $this->cookies->has($this->session->getName()); + return $this->hasSession() && $this->cookies->has($this->getSession()->getName()); } /** @@ -865,16 +757,19 @@ public function hasSession() return null !== $this->session; } - /** - * Sets the Session. - * - * @param SessionInterface $session The Session - */ public function setSession(SessionInterface $session) { $this->session = $session; } + /** + * @internal + */ + public function setSessionFactory(callable $factory) + { + $this->session = $factory; + } + /** * Returns the client IP addresses. * @@ -893,10 +788,10 @@ public function getClientIps() $ip = $this->server->get('REMOTE_ADDR'); if (!$this->isFromTrustedProxy()) { - return array($ip); + return [$ip]; } - return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip); + return $this->getTrustedValues(self::HEADER_X_FORWARDED_FOR, $ip) ?: [$ip]; } /** @@ -915,7 +810,7 @@ public function getClientIps() * @return string|null The client IP address * * @see getClientIps() - * @see http://en.wikipedia.org/wiki/X-Forwarded-For + * @see https://wikipedia.org/wiki/X-Forwarded-For */ public function getClientIp() { @@ -1015,17 +910,13 @@ public function getScheme() * * The "X-Forwarded-Port" header must contain the client port. * - * If your reverse proxy uses a different header name than "X-Forwarded-Port", - * configure it via via the $trustedHeaderSet argument of the - * Request::setTrustedProxies() method instead. - * * @return int|string can be a string if fetched from the server bag */ public function getPort() { - if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_PORT)) { + if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_PORT)) { $host = $host[0]; - } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) { + } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) { $host = $host[0]; } elseif (!$host = $this->headers->get('HOST')) { return $this->server->get('SERVER_PORT'); @@ -1037,8 +928,8 @@ public function getPort() $pos = strrpos($host, ':'); } - if (false !== $pos) { - return (int) substr($host, $pos + 1); + if (false !== $pos && $port = substr($host, $pos + 1)) { + return (int) $port; } return 'https' === $this->getScheme() ? 443 : 80; @@ -1204,7 +1095,7 @@ public function getRelativeUriForPath($path) // A reference to the same base directory or an empty subdirectory must be prefixed with "./". // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used // as the first segment of a relative-path reference, as it would be mistaken for a scheme name - // (see http://tools.ietf.org/html/rfc3986#section-4.2). + // (see https://tools.ietf.org/html/rfc3986#section-4.2). return !isset($path[0]) || '/' === $path[0] || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) ? "./$path" : $path; @@ -1233,16 +1124,12 @@ public function getQueryString() * * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". * - * If your reverse proxy uses a different header name than "X-Forwarded-Proto" - * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet - * argument of the Request::setTrustedProxies() method instead. - * * @return bool */ public function isSecure() { - if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) { - return \in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true); + if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_X_FORWARDED_PROTO)) { + return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true); } $https = $this->server->get('HTTPS'); @@ -1258,17 +1145,13 @@ public function isSecure() * * The "X-Forwarded-Host" header must contain the client host name. * - * If your reverse proxy uses a different header name than "X-Forwarded-Host", - * configure it via the $trustedHeaderSet argument of the - * Request::setTrustedProxies() method instead. - * * @return string * * @throws SuspiciousOperationException when the host name is invalid or not trusted */ public function getHost() { - if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) { + if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) { $host = $host[0]; } elseif (!$host = $this->headers->get('HOST')) { if (!$host = $this->server->get('SERVER_NAME')) { @@ -1346,22 +1229,37 @@ public function setMethod($method) */ public function getMethod() { - if (null === $this->method) { - $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + if (null !== $this->method) { + return $this->method; + } - if ('POST' === $this->method) { - if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) { - $this->method = strtoupper($method); - } elseif (self::$httpMethodParameterOverride) { - $method = $this->request->get('_method', $this->query->get('_method', 'POST')); - if (\is_string($method)) { - $this->method = strtoupper($method); - } - } - } + $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + + if ('POST' !== $this->method) { + return $this->method; } - return $this->method; + $method = $this->headers->get('X-HTTP-METHOD-OVERRIDE'); + + if (!$method && self::$httpMethodParameterOverride) { + $method = $this->request->get('_method', $this->query->get('_method', 'POST')); + } + + if (!\is_string($method)) { + return $this->method; + } + + $method = strtoupper($method); + + if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE'], true)) { + return $this->method = $method; + } + + if (!preg_match('/^[A-Z]++$/D', $method)) { + throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method)); + } + + return $this->method = $method; } /** @@ -1405,7 +1303,7 @@ public static function getMimeTypes($format) static::initializeFormats(); } - return isset(static::$formats[$format]) ? static::$formats[$format] : array(); + return isset(static::$formats[$format]) ? static::$formats[$format] : []; } /** @@ -1434,6 +1332,8 @@ public function getFormat($mimeType) return $format; } } + + return null; } /** @@ -1448,7 +1348,7 @@ public function setFormat($format, $mimeTypes) static::initializeFormats(); } - static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); + static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes]; } /** @@ -1460,9 +1360,11 @@ public function setFormat($format, $mimeTypes) * * _format request attribute * * $default * + * @see getPreferredFormat + * * @param string|null $default The default format * - * @return string The request format + * @return string|null The request format */ public function getRequestFormat($default = 'html') { @@ -1554,21 +1456,15 @@ public function isMethod($method) * * @see https://tools.ietf.org/html/rfc7231#section-4.2.1 * - * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default. - * * @return bool */ - public function isMethodSafe(/* $andCacheable = true */) + public function isMethodSafe() { - if (!\func_num_args() || func_get_arg(0)) { - // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature) - // then setting $andCacheable to false should be deprecated in 4.1 - @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED); - - return \in_array($this->getMethod(), array('GET', 'HEAD')); + if (\func_num_args() > 0) { + @trigger_error(sprintf('Passing arguments to "%s()" has been deprecated since Symfony 4.4; use "%s::isMethodCacheable()" to check if the method is cacheable instead.', __METHOD__, __CLASS__), E_USER_DEPRECATED); } - return \in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); + return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']); } /** @@ -1578,7 +1474,7 @@ public function isMethodSafe(/* $andCacheable = true */) */ public function isMethodIdempotent() { - return \in_array($this->getMethod(), array('HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE')); + return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']); } /** @@ -1590,7 +1486,7 @@ public function isMethodIdempotent() */ public function isMethodCacheable() { - return \in_array($this->getMethod(), array('GET', 'HEAD')); + return \in_array($this->getMethod(), ['GET', 'HEAD']); } /** @@ -1629,9 +1525,6 @@ public function getProtocolVersion() public function getContent($asResource = false) { $currentContentIsResource = \is_resource($this->content); - if (\PHP_VERSION_ID < 50600 && false === $this->content) { - throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.'); - } if (true === $asResource) { if ($currentContentIsResource) { @@ -1685,10 +1578,31 @@ public function isNoCache() return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); } + /** + * Gets the preferred format for the response by inspecting, in the following order: + * * the request format set using setRequestFormat + * * the values of the Accept HTTP header + * * the content type of the body of the request. + */ + public function getPreferredFormat(?string $default = 'html'): ?string + { + if (null !== $this->preferredFormat || null !== $this->preferredFormat = $this->getRequestFormat(null)) { + return $this->preferredFormat; + } + + foreach ($this->getAcceptableContentTypes() as $mimeType) { + if ($this->preferredFormat = $this->getFormat($mimeType)) { + return $this->preferredFormat; + } + } + + return $default; + } + /** * Returns the preferred language. * - * @param array $locales An array of ordered available locales + * @param string[] $locales An array of ordered available locales * * @return string|null The preferred locale */ @@ -1704,7 +1618,7 @@ public function getPreferredLanguage(array $locales = null) return $locales[0]; } - $extendedPreferredLanguages = array(); + $extendedPreferredLanguages = []; foreach ($preferredLanguages as $language) { $extendedPreferredLanguages[] = $language; if (false !== $position = strpos($language, '_')) { @@ -1732,7 +1646,7 @@ public function getLanguages() } $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); - $this->languages = array(); + $this->languages = []; foreach ($languages as $lang => $acceptHeaderItem) { if (false !== strpos($lang, '-')) { $codes = explode('-', $lang); @@ -1808,7 +1722,7 @@ public function getAcceptableContentTypes() * It works if your JavaScript library sets an X-Requested-With HTTP header. * It is known to work with common JavaScript frameworks: * - * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript + * @see https://wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript * * @return bool true if the request is an XMLHttpRequest, false otherwise */ @@ -1820,9 +1734,9 @@ public function isXmlHttpRequest() /* * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24) * - * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd). + * Code subject to the new BSD license (https://framework.zend.com/license). * - * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (https://www.zend.com/) */ protected function prepareRequestUri() @@ -1908,12 +1822,12 @@ protected function prepareBaseUrl() $requestUri = '/'.$requestUri; } - if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { + if ($baseUrl && null !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { // full $baseUrl matches return $prefix; } - if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) { + if ($baseUrl && null !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) { // directory portion of $baseUrl matches return rtrim($prefix, '/'.\DIRECTORY_SEPARATOR); } @@ -2002,27 +1916,22 @@ protected function preparePathInfo() */ protected static function initializeFormats() { - static::$formats = array( - 'html' => array('text/html', 'application/xhtml+xml'), - 'txt' => array('text/plain'), - 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), - 'css' => array('text/css'), - 'json' => array('application/json', 'application/x-json'), - 'jsonld' => array('application/ld+json'), - 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), - 'rdf' => array('application/rdf+xml'), - 'atom' => array('application/atom+xml'), - 'rss' => array('application/rss+xml'), - 'form' => array('application/x-www-form-urlencoded'), - ); + static::$formats = [ + 'html' => ['text/html', 'application/xhtml+xml'], + 'txt' => ['text/plain'], + 'js' => ['application/javascript', 'application/x-javascript', 'text/javascript'], + 'css' => ['text/css'], + 'json' => ['application/json', 'application/x-json'], + 'jsonld' => ['application/ld+json'], + 'xml' => ['text/xml', 'application/xml', 'application/x-xml'], + 'rdf' => ['application/rdf+xml'], + 'atom' => ['application/atom+xml'], + 'rss' => ['application/rss+xml'], + 'form' => ['application/x-www-form-urlencoded'], + ]; } - /** - * Sets the default PHP locale. - * - * @param string $locale - */ - private function setPhpDefaultLocale($locale) + private function setPhpDefaultLocale(string $locale): void { // if either the class Locale doesn't exist, or an exception is thrown when // setting the default locale, the intl module is not installed, and @@ -2035,19 +1944,14 @@ private function setPhpDefaultLocale($locale) } } - /* + /** * Returns the prefix as encoded in the string when the string starts with - * the given prefix, false otherwise. - * - * @param string $string The urlencoded string - * @param string $prefix The prefix not encoded - * - * @return string|false The prefix as it is encoded in $string, or false + * the given prefix, null otherwise. */ - private function getUrlencodedPrefix($string, $prefix) + private function getUrlencodedPrefix(string $string, string $prefix): ?string { if (0 !== strpos(rawurldecode($string), $prefix)) { - return false; + return null; } $len = \strlen($prefix); @@ -2056,13 +1960,13 @@ private function getUrlencodedPrefix($string, $prefix) return $match[0]; } - return false; + return null; } - private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): self { if (self::$requestFactory) { - $request = \call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); + $request = (self::$requestFactory)($query, $request, $attributes, $cookies, $files, $server, $content); if (!$request instanceof self) { throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); @@ -2087,27 +1991,33 @@ public function isFromTrustedProxy() return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies); } - private function getTrustedValues($type, $ip = null) + private function getTrustedValues(int $type, string $ip = null): array { - $clientValues = array(); - $forwardedValues = array(); + $clientValues = []; + $forwardedValues = []; - if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) { + if ((self::$trustedHeaderSet & $type) && $this->headers->has(self::$trustedHeaders[$type])) { foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) { - $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v); + $clientValues[] = (self::HEADER_X_FORWARDED_PORT === $type ? '0.0.0.0:' : '').trim($v); } } - if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { - $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); - $forwardedValues = preg_match_all(sprintf('{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array(); - if (self::HEADER_CLIENT_PORT === $type) { - foreach ($forwardedValues as $k => $v) { + if ((self::$trustedHeaderSet & self::HEADER_FORWARDED) && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { + $forwarded = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); + $parts = HeaderUtils::split($forwarded, ',;='); + $forwardedValues = []; + $param = self::$forwardedParams[$type]; + foreach ($parts as $subParts) { + if (null === $v = HeaderUtils::combine($subParts)[$param] ?? null) { + continue; + } + if (self::HEADER_X_FORWARDED_PORT === $type) { if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) { $v = $this->isSecure() ? ':443' : ':80'; } - $forwardedValues[$k] = '0.0.0.0'.$v; + $v = '0.0.0.0'.$v; } + $forwardedValues[] = $v; } } @@ -2125,17 +2035,17 @@ private function getTrustedValues($type, $ip = null) } if (!$this->isForwardedValid) { - return null !== $ip ? array('0.0.0.0', $ip) : array(); + return null !== $ip ? ['0.0.0.0', $ip] : []; } $this->isForwardedValid = false; throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type])); } - private function normalizeAndFilterClientIps(array $clientIps, $ip) + private function normalizeAndFilterClientIps(array $clientIps, string $ip): array { if (!$clientIps) { - return array(); + return []; } $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from $firstTrustedIp = null; @@ -2171,6 +2081,6 @@ private function normalizeAndFilterClientIps(array $clientIps, $ip) } // Now the IP chain contains only untrusted proxies and the client IP - return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp); + return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp]; } } diff --git a/vendor/symfony/http-foundation/RequestMatcher.php b/vendor/symfony/http-foundation/RequestMatcher.php index 6b4cef1..9a4a2a1 100644 --- a/vendor/symfony/http-foundation/RequestMatcher.php +++ b/vendor/symfony/http-foundation/RequestMatcher.php @@ -28,41 +28,44 @@ class RequestMatcher implements RequestMatcherInterface */ private $host; + /** + * @var int|null + */ + private $port; + /** * @var string[] */ - private $methods = array(); + private $methods = []; /** * @var string[] */ - private $ips = array(); + private $ips = []; /** * @var array */ - private $attributes = array(); + private $attributes = []; /** * @var string[] */ - private $schemes = array(); + private $schemes = []; /** - * @param string|null $path - * @param string|null $host * @param string|string[]|null $methods * @param string|string[]|null $ips - * @param array $attributes * @param string|string[]|null $schemes */ - public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = 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); $this->matchMethod($methods); $this->matchIps($ips); $this->matchScheme($schemes); + $this->matchPort($port); foreach ($attributes as $k => $v) { $this->matchAttribute($k, $v); @@ -76,7 +79,7 @@ public function __construct($path = null, $host = null, $methods = null, $ips = */ public function matchScheme($scheme) { - $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array(); + $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : []; } /** @@ -89,6 +92,16 @@ public function matchHost($regexp) $this->host = $regexp; } + /** + * Adds a check for the the URL port. + * + * @param int|null $port The port number to connect to + */ + public function matchPort(?int $port) + { + $this->port = $port; + } + /** * Adds a check for the URL path info. * @@ -116,7 +129,7 @@ public function matchIp($ip) */ public function matchIps($ips) { - $this->ips = null !== $ips ? (array) $ips : array(); + $this->ips = null !== $ips ? (array) $ips : []; } /** @@ -126,7 +139,7 @@ public function matchIps($ips) */ public function matchMethod($method) { - $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array(); + $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : []; } /** @@ -167,6 +180,10 @@ public function matches(Request $request) return false; } + if (null !== $this->port && 0 < $this->port && $request->getPort() !== $this->port) { + return false; + } + if (IpUtils::checkIp($request->getClientIp(), $this->ips)) { return true; } diff --git a/vendor/symfony/http-foundation/RequestStack.php b/vendor/symfony/http-foundation/RequestStack.php index 40123f6..244a77d 100644 --- a/vendor/symfony/http-foundation/RequestStack.php +++ b/vendor/symfony/http-foundation/RequestStack.php @@ -21,7 +21,7 @@ class RequestStack /** * @var Request[] */ - private $requests = array(); + private $requests = []; /** * Pushes a Request on the stack. @@ -47,7 +47,7 @@ public function push(Request $request) public function pop() { if (!$this->requests) { - return; + return null; } return array_pop($this->requests); @@ -73,7 +73,7 @@ public function getCurrentRequest() public function getMasterRequest() { if (!$this->requests) { - return; + return null; } return $this->requests[0]; @@ -95,7 +95,7 @@ public function getParentRequest() $pos = \count($this->requests) - 2; if (!isset($this->requests[$pos])) { - return; + return null; } return $this->requests[$pos]; diff --git a/vendor/symfony/http-foundation/Response.php b/vendor/symfony/http-foundation/Response.php index f267d1e..b267cd8 100644 --- a/vendor/symfony/http-foundation/Response.php +++ b/vendor/symfony/http-foundation/Response.php @@ -88,7 +88,7 @@ class Response const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585 /** - * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag + * @var ResponseHeaderBag */ public $headers; @@ -121,14 +121,14 @@ class Response * Status codes translation table. * * The list of codes is complete according to the - * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry} + * {@link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml Hypertext Transfer Protocol (HTTP) Status Code Registry} * (last updated 2016-03-01). * * Unless otherwise noted, the status code is defined in RFC2616. * * @var array */ - public static $statusTexts = array( + public static $statusTexts = [ 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', // RFC2518 @@ -191,16 +191,12 @@ class Response 508 => 'Loop Detected', // RFC5842 510 => 'Not Extended', // RFC2774 511 => 'Network Authentication Required', // RFC6585 - ); + ]; /** - * @param mixed $content The response content, see setContent() - * @param int $status The response status code - * @param array $headers An array of response headers - * * @throws \InvalidArgumentException When the HTTP status code is not valid */ - public function __construct($content = '', $status = 200, $headers = array()) + public function __construct($content = '', int $status = 200, array $headers = []) { $this->headers = new ResponseHeaderBag($headers); $this->setContent($content); @@ -222,7 +218,7 @@ public function __construct($content = '', $status = 200, $headers = array()) * * @return static */ - public static function create($content = '', $status = 200, $headers = array()) + public static function create($content = '', $status = 200, $headers = []) { return new static($content, $status, $headers); } @@ -271,10 +267,12 @@ public function prepare(Request $request) $this->setContent(null); $headers->remove('Content-Type'); $headers->remove('Content-Length'); + // prevent PHP from sending the Content-Type header based on default_mimetype + ini_set('default_mimetype', ''); } else { // Content-type based on the Request if (!$headers->has('Content-Type')) { - $format = $request->getRequestFormat(); + $format = $request->getPreferredFormat(null); if (null !== $format && $mimeType = $request->getMimeType($format)) { $headers->set('Content-Type', $mimeType); } @@ -310,13 +308,19 @@ public function prepare(Request $request) } // Check if we need to send extra expire info headers - if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) { - $this->headers->set('pragma', 'no-cache'); - $this->headers->set('expires', -1); + if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) { + $headers->set('pragma', 'no-cache'); + $headers->set('expires', -1); } $this->ensureIEOverSSLCompatibility($request); + if ($request->isSecure()) { + foreach ($headers->getCookies() as $cookie) { + $cookie->setSecureDefault(true); + } + } + return $this; } @@ -342,7 +346,7 @@ public function sendHeaders() // cookies foreach ($this->headers->getCookies() as $cookie) { - header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode); + header('Set-Cookie: '.$cookie, false, $this->statusCode); } // status @@ -375,7 +379,7 @@ public function send() if (\function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); - } elseif (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) { + } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); } @@ -395,7 +399,7 @@ public function send() */ public function setContent($content) { - if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable(array($content, '__toString'))) { + if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) { throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content))); } @@ -407,7 +411,7 @@ public function setContent($content) /** * Gets the current response content. * - * @return string Content + * @return string|false */ public function getContent() { @@ -417,13 +421,11 @@ public function getContent() /** * Sets the HTTP protocol version (1.0 or 1.1). * - * @param string $version The HTTP protocol version - * * @return $this * - * @final since version 3.2 + * @final */ - public function setProtocolVersion($version) + public function setProtocolVersion(string $version) { $this->version = $version; @@ -433,11 +435,9 @@ public function setProtocolVersion($version) /** * Gets the HTTP protocol version. * - * @return string The HTTP protocol version - * - * @final since version 3.2 + * @final */ - public function getProtocolVersion() + public function getProtocolVersion(): string { return $this->version; } @@ -448,18 +448,15 @@ public function getProtocolVersion() * If the status text is null it will be automatically populated for the known * status codes and left empty otherwise. * - * @param int $code HTTP status code - * @param mixed $text HTTP status text - * * @return $this * * @throws \InvalidArgumentException When the HTTP status code is not valid * - * @final since version 3.2 + * @final */ - public function setStatusCode($code, $text = null) + public function setStatusCode(int $code, $text = null) { - $this->statusCode = $code = (int) $code; + $this->statusCode = $code; if ($this->isInvalid()) { throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); } @@ -484,11 +481,9 @@ public function setStatusCode($code, $text = null) /** * Retrieves the status code for the current web response. * - * @return int Status code - * - * @final since version 3.2 + * @final */ - public function getStatusCode() + public function getStatusCode(): int { return $this->statusCode; } @@ -496,13 +491,11 @@ public function getStatusCode() /** * Sets the response charset. * - * @param string $charset Character set - * * @return $this * - * @final since version 3.2 + * @final */ - public function setCharset($charset) + public function setCharset(string $charset) { $this->charset = $charset; @@ -512,11 +505,9 @@ public function setCharset($charset) /** * Retrieves the response charset. * - * @return string Character set - * - * @final since version 3.2 + * @final */ - public function getCharset() + public function getCharset(): ?string { return $this->charset; } @@ -536,13 +527,11 @@ public function getCharset() * can be reused by a cache with heuristic expiration unless otherwise indicated" * (https://tools.ietf.org/html/rfc7231#section-6.1) * - * @return bool true if the response is worth caching, false otherwise - * - * @final since version 3.3 + * @final */ - public function isCacheable() + public function isCacheable(): bool { - if (!\in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { + if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) { return false; } @@ -560,11 +549,9 @@ public function isCacheable() * origin. A response is considered fresh when it includes a Cache-Control/max-age * indicator or Expires header and the calculated age is less than the freshness lifetime. * - * @return bool true if the response is fresh, false otherwise - * - * @final since version 3.3 + * @final */ - public function isFresh() + public function isFresh(): bool { return $this->getTtl() > 0; } @@ -573,11 +560,9 @@ public function isFresh() * Returns true if the response includes headers that can be used to validate * the response with the origin server using a conditional GET request. * - * @return bool true if the response is validateable, false otherwise - * - * @final since version 3.3 + * @final */ - public function isValidateable() + public function isValidateable(): bool { return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); } @@ -589,7 +574,7 @@ public function isValidateable() * * @return $this * - * @final since version 3.2 + * @final */ public function setPrivate() { @@ -606,7 +591,7 @@ public function setPrivate() * * @return $this * - * @final since version 3.2 + * @final */ public function setPublic() { @@ -619,13 +604,11 @@ public function setPublic() /** * Marks the response as "immutable". * - * @param bool $immutable enables or disables the immutable directive - * * @return $this * * @final */ - public function setImmutable($immutable = true) + public function setImmutable(bool $immutable = true) { if ($immutable) { $this->headers->addCacheControlDirective('immutable'); @@ -639,28 +622,24 @@ public function setImmutable($immutable = true) /** * Returns true if the response is marked as "immutable". * - * @return bool returns true if the response is marked as "immutable"; otherwise false - * * @final */ - public function isImmutable() + public function isImmutable(): bool { return $this->headers->hasCacheControlDirective('immutable'); } /** - * Returns true if the response must be revalidated by caches. + * Returns true if the response must be revalidated by shared caches once it has become stale. * * This method indicates that the response must not be served stale by a * cache in any circumstance without first revalidating with the origin. * When present, the TTL of the response should not be overridden to be * greater than the value provided by the origin. * - * @return bool true if the response must be revalidated by a cache, false otherwise - * - * @final since version 3.3 + * @final */ - public function mustRevalidate() + public function mustRevalidate(): bool { return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); } @@ -668,13 +647,11 @@ public function mustRevalidate() /** * Returns the Date header as a DateTime instance. * - * @return \DateTime A \DateTime instance - * * @throws \RuntimeException When the header is not parseable * - * @final since version 3.2 + * @final */ - public function getDate() + public function getDate(): ?\DateTimeInterface { return $this->headers->getDate('Date'); } @@ -684,30 +661,32 @@ public function getDate() * * @return $this * - * @final since version 3.2 + * @final */ - public function setDate(\DateTime $date) + public function setDate(\DateTimeInterface $date) { - $date->setTimezone(new \DateTimeZone('UTC')); + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); return $this; } /** - * Returns the age of the response. - * - * @return int The age of the response in seconds + * Returns the age of the response in seconds. * - * @final since version 3.2 + * @final */ - public function getAge() + public function getAge(): int { if (null !== $age = $this->headers->get('Age')) { return (int) $age; } - return max(time() - $this->getDate()->format('U'), 0); + return max(time() - (int) $this->getDate()->format('U'), 0); } /** @@ -728,17 +707,15 @@ public function expire() /** * Returns the value of the Expires header as a DateTime instance. * - * @return \DateTime|null A DateTime instance or null if the header does not exist - * - * @final since version 3.2 + * @final */ - public function getExpires() + public function getExpires(): ?\DateTimeInterface { try { return $this->headers->getDate('Expires'); } catch (\RuntimeException $e) { // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past - return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000'); + return \DateTime::createFromFormat('U', time() - 172800); } } @@ -747,22 +724,25 @@ public function getExpires() * * Passing null as value will remove the header. * - * @param \DateTime|null $date A \DateTime instance or null to remove the header - * * @return $this * - * @final since version 3.2 + * @final */ - public function setExpires(\DateTime $date = null) + public function setExpires(\DateTimeInterface $date = null) { if (null === $date) { $this->headers->remove('Expires'); - } else { - $date = clone $date; - $date->setTimezone(new \DateTimeZone('UTC')); - $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; } + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + return $this; } @@ -773,11 +753,9 @@ public function setExpires(\DateTime $date = null) * First, it checks for a s-maxage directive, then a max-age directive, and then it falls * back on an expires header. It returns null when no maximum age can be established. * - * @return int|null Number of seconds - * - * @final since version 3.2 + * @final */ - public function getMaxAge() + public function getMaxAge(): ?int { if ($this->headers->hasCacheControlDirective('s-maxage')) { return (int) $this->headers->getCacheControlDirective('s-maxage'); @@ -788,8 +766,10 @@ public function getMaxAge() } if (null !== $this->getExpires()) { - return $this->getExpires()->format('U') - $this->getDate()->format('U'); + return (int) $this->getExpires()->format('U') - (int) $this->getDate()->format('U'); } + + return null; } /** @@ -797,13 +777,11 @@ public function getMaxAge() * * This methods sets the Cache-Control max-age directive. * - * @param int $value Number of seconds - * * @return $this * - * @final since version 3.2 + * @final */ - public function setMaxAge($value) + public function setMaxAge(int $value) { $this->headers->addCacheControlDirective('max-age', $value); @@ -815,13 +793,11 @@ public function setMaxAge($value) * * This methods sets the Cache-Control s-maxage directive. * - * @param int $value Number of seconds - * * @return $this * - * @final since version 3.2 + * @final */ - public function setSharedMaxAge($value) + public function setSharedMaxAge(int $value) { $this->setPublic(); $this->headers->addCacheControlDirective('s-maxage', $value); @@ -837,29 +813,25 @@ public function setSharedMaxAge($value) * When the responses TTL is <= 0, the response may not be served from cache without first * revalidating with the origin. * - * @return int|null The TTL in seconds - * - * @final since version 3.2 + * @final */ - public function getTtl() + public function getTtl(): ?int { - if (null !== $maxAge = $this->getMaxAge()) { - return $maxAge - $this->getAge(); - } + $maxAge = $this->getMaxAge(); + + return null !== $maxAge ? $maxAge - $this->getAge() : null; } /** - * Sets the response's time-to-live for shared caches. + * Sets the response's time-to-live for shared caches in seconds. * * This method adjusts the Cache-Control/s-maxage directive. * - * @param int $seconds Number of seconds - * * @return $this * - * @final since version 3.2 + * @final */ - public function setTtl($seconds) + public function setTtl(int $seconds) { $this->setSharedMaxAge($this->getAge() + $seconds); @@ -867,17 +839,15 @@ public function setTtl($seconds) } /** - * Sets the response's time-to-live for private/client caches. + * Sets the response's time-to-live for private/client caches in seconds. * * This method adjusts the Cache-Control/max-age directive. * - * @param int $seconds Number of seconds - * * @return $this * - * @final since version 3.2 + * @final */ - public function setClientTtl($seconds) + public function setClientTtl(int $seconds) { $this->setMaxAge($this->getAge() + $seconds); @@ -887,13 +857,11 @@ public function setClientTtl($seconds) /** * Returns the Last-Modified HTTP header as a DateTime instance. * - * @return \DateTime|null A DateTime instance or null if the header does not exist - * * @throws \RuntimeException When the HTTP header is not parseable * - * @final since version 3.2 + * @final */ - public function getLastModified() + public function getLastModified(): ?\DateTimeInterface { return $this->headers->getDate('Last-Modified'); } @@ -903,33 +871,34 @@ public function getLastModified() * * Passing null as value will remove the header. * - * @param \DateTime|null $date A \DateTime instance or null to remove the header - * * @return $this * - * @final since version 3.2 + * @final */ - public function setLastModified(\DateTime $date = null) + public function setLastModified(\DateTimeInterface $date = null) { if (null === $date) { $this->headers->remove('Last-Modified'); - } else { - $date = clone $date; - $date->setTimezone(new \DateTimeZone('UTC')); - $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; } + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + return $this; } /** * Returns the literal value of the ETag HTTP header. * - * @return string|null The ETag HTTP header or null if it does not exist - * - * @final since version 3.2 + * @final */ - public function getEtag() + public function getEtag(): ?string { return $this->headers->get('ETag'); } @@ -942,9 +911,9 @@ public function getEtag() * * @return $this * - * @final since version 3.2 + * @final */ - public function setEtag($etag = null, $weak = false) + public function setEtag(string $etag = null, bool $weak = false) { if (null === $etag) { $this->headers->remove('Etag'); @@ -964,17 +933,15 @@ public function setEtag($etag = null, $weak = false) * * Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable. * - * @param array $options An array of cache options - * * @return $this * * @throws \InvalidArgumentException * - * @final since version 3.3 + * @final */ public function setCache(array $options) { - if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) { + if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) { throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff))); } @@ -1025,9 +992,9 @@ public function setCache(array $options) * * @return $this * - * @see http://tools.ietf.org/html/rfc2616#section-10.3.5 + * @see https://tools.ietf.org/html/rfc2616#section-10.3.5 * - * @final since version 3.3 + * @final */ public function setNotModified() { @@ -1035,7 +1002,7 @@ public function setNotModified() $this->setContent(null); // remove headers that MUST NOT be included with 304 Not Modified responses - foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) { + foreach (['Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified'] as $header) { $this->headers->remove($header); } @@ -1045,11 +1012,9 @@ public function setNotModified() /** * Returns true if the response includes a Vary header. * - * @return bool true if the response includes a Vary header, false otherwise - * - * @final since version 3.2 + * @final */ - public function hasVary() + public function hasVary(): bool { return null !== $this->headers->get('Vary'); } @@ -1057,17 +1022,15 @@ public function hasVary() /** * Returns an array of header names given in the Vary header. * - * @return array An array of Vary names - * - * @final since version 3.2 + * @final */ - public function getVary() + public function getVary(): array { - if (!$vary = $this->headers->get('Vary', null, false)) { - return array(); + if (!$vary = $this->headers->all('Vary')) { + return []; } - $ret = array(); + $ret = []; foreach ($vary as $item) { $ret = array_merge($ret, preg_split('/[\s,]+/', $item)); } @@ -1083,9 +1046,9 @@ public function getVary() * * @return $this * - * @final since version 3.2 + * @final */ - public function setVary($headers, $replace = true) + public function setVary($headers, bool $replace = true) { $this->headers->set('Vary', $headers, $replace); @@ -1101,9 +1064,9 @@ public function setVary($headers, $replace = true) * * @return bool true if the Response validators match the Request, false otherwise * - * @final since version 3.3 + * @final */ - public function isNotModified(Request $request) + public function isNotModified(Request $request): bool { if (!$request->isMethodCacheable()) { return false; @@ -1131,13 +1094,11 @@ public function isNotModified(Request $request) /** * Is response invalid? * - * @return bool - * - * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html * - * @final since version 3.2 + * @final */ - public function isInvalid() + public function isInvalid(): bool { return $this->statusCode < 100 || $this->statusCode >= 600; } @@ -1145,11 +1106,9 @@ public function isInvalid() /** * Is response informative? * - * @return bool - * - * @final since version 3.3 + * @final */ - public function isInformational() + public function isInformational(): bool { return $this->statusCode >= 100 && $this->statusCode < 200; } @@ -1157,11 +1116,9 @@ public function isInformational() /** * Is response successful? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isSuccessful() + public function isSuccessful(): bool { return $this->statusCode >= 200 && $this->statusCode < 300; } @@ -1169,11 +1126,9 @@ public function isSuccessful() /** * Is the response a redirect? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isRedirection() + public function isRedirection(): bool { return $this->statusCode >= 300 && $this->statusCode < 400; } @@ -1181,11 +1136,9 @@ public function isRedirection() /** * Is there a client error? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isClientError() + public function isClientError(): bool { return $this->statusCode >= 400 && $this->statusCode < 500; } @@ -1193,11 +1146,9 @@ public function isClientError() /** * Was there a server side error? * - * @return bool - * - * @final since version 3.3 + * @final */ - public function isServerError() + public function isServerError(): bool { return $this->statusCode >= 500 && $this->statusCode < 600; } @@ -1205,11 +1156,9 @@ public function isServerError() /** * Is the response OK? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isOk() + public function isOk(): bool { return 200 === $this->statusCode; } @@ -1217,11 +1166,9 @@ public function isOk() /** * Is the response forbidden? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isForbidden() + public function isForbidden(): bool { return 403 === $this->statusCode; } @@ -1229,11 +1176,9 @@ public function isForbidden() /** * Is the response a not found error? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isNotFound() + public function isNotFound(): bool { return 404 === $this->statusCode; } @@ -1241,27 +1186,21 @@ public function isNotFound() /** * Is the response a redirect of some form? * - * @param string $location - * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isRedirect($location = null) + public function isRedirect(string $location = null): bool { - return \in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); + return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location')); } /** * Is the response empty? * - * @return bool - * - * @final since version 3.2 + * @final */ - public function isEmpty() + public function isEmpty(): bool { - return \in_array($this->statusCode, array(204, 304)); + return \in_array($this->statusCode, [204, 304]); } /** @@ -1269,17 +1208,13 @@ public function isEmpty() * * Resulting level can be greater than target level if a non-removable buffer has been encountered. * - * @param int $targetLevel The target output buffering level - * @param bool $flush Whether to flush or clean the buffers - * - * @final since version 3.3 + * @final */ - public static function closeOutputBuffers($targetLevel, $flush) + public static function closeOutputBuffers(int $targetLevel, bool $flush): void { $status = ob_get_status(true); $level = \count($status); - // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3 - $flags = \defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1; + $flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE); while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) { if ($flush) { @@ -1295,9 +1230,9 @@ public static function closeOutputBuffers($targetLevel, $flush) * * @see http://support.microsoft.com/kb/323308 * - * @final since version 3.3 + * @final */ - protected function ensureIEOverSSLCompatibility(Request $request) + protected function ensureIEOverSSLCompatibility(Request $request): void { if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) { if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) { diff --git a/vendor/symfony/http-foundation/ResponseHeaderBag.php b/vendor/symfony/http-foundation/ResponseHeaderBag.php index 00cff7a..cdc6699 100644 --- a/vendor/symfony/http-foundation/ResponseHeaderBag.php +++ b/vendor/symfony/http-foundation/ResponseHeaderBag.php @@ -24,11 +24,11 @@ class ResponseHeaderBag extends HeaderBag const DISPOSITION_ATTACHMENT = 'attachment'; const DISPOSITION_INLINE = 'inline'; - protected $computedCacheControl = array(); - protected $cookies = array(); - protected $headerNames = array(); + protected $computedCacheControl = []; + protected $cookies = []; + protected $headerNames = []; - public function __construct(array $headers = array()) + public function __construct(array $headers = []) { parent::__construct($headers); @@ -49,9 +49,9 @@ public function __construct(array $headers = array()) */ public function allPreserveCase() { - $headers = array(); + $headers = []; foreach ($this->all() as $name => $value) { - $headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value; + $headers[$this->headerNames[$name] ?? $name] = $value; } return $headers; @@ -70,9 +70,9 @@ public function allPreserveCaseWithoutCookies() /** * {@inheritdoc} */ - public function replace(array $headers = array()) + public function replace(array $headers = []) { - $this->headerNames = array(); + $this->headerNames = []; parent::replace($headers); @@ -87,10 +87,19 @@ public function replace(array $headers = array()) /** * {@inheritdoc} + * + * @param string|null $key The name of the headers to return or null to get them all */ - public function all() + public function all(/*string $key = null*/) { $headers = parent::all(); + + if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) { + $key = strtr($key, self::UPPER, self::LOWER); + + return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies()); + } + foreach ($this->getCookies() as $cookie) { $headers['set-cookie'][] = (string) $cookie; } @@ -103,11 +112,11 @@ public function all() */ public function set($key, $values, $replace = true) { - $uniqueKey = str_replace('_', '-', strtolower($key)); + $uniqueKey = strtr($key, self::UPPER, self::LOWER); if ('set-cookie' === $uniqueKey) { if ($replace) { - $this->cookies = array(); + $this->cookies = []; } foreach ((array) $values as $cookie) { $this->setCookie(Cookie::fromString($cookie)); @@ -122,9 +131,8 @@ public function set($key, $values, $replace = true) parent::set($key, $values, $replace); // ensure the cache-control header has sensible defaults - if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'), true)) { - $computed = $this->computeCacheControlValue(); - $this->headers['cache-control'] = array($computed); + if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) { + $this->headers['cache-control'] = [$computed]; $this->headerNames['cache-control'] = 'Cache-Control'; $this->computedCacheControl = $this->parseCacheControl($computed); } @@ -135,11 +143,11 @@ public function set($key, $values, $replace = true) */ public function remove($key) { - $uniqueKey = str_replace('_', '-', strtolower($key)); + $uniqueKey = strtr($key, self::UPPER, self::LOWER); unset($this->headerNames[$uniqueKey]); if ('set-cookie' === $uniqueKey) { - $this->cookies = array(); + $this->cookies = []; return; } @@ -147,7 +155,7 @@ public function remove($key) parent::remove($key); if ('cache-control' === $uniqueKey) { - $this->computedCacheControl = array(); + $this->computedCacheControl = []; } if ('date' === $uniqueKey) { @@ -160,7 +168,7 @@ public function remove($key) */ public function hasCacheControlDirective($key) { - return array_key_exists($key, $this->computedCacheControl); + return \array_key_exists($key, $this->computedCacheControl); } /** @@ -168,7 +176,7 @@ public function hasCacheControlDirective($key) */ public function getCacheControlDirective($key) { - return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; + return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; } public function setCookie(Cookie $cookie) @@ -216,15 +224,15 @@ public function removeCookie($name, $path = '/', $domain = null) */ public function getCookies($format = self::COOKIES_FLAT) { - if (!\in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) { - throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY)))); + if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) { + throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY]))); } if (self::COOKIES_ARRAY === $format) { return $this->cookies; } - $flattenedCookies = array(); + $flattenedCookies = []; foreach ($this->cookies as $path) { foreach ($path as $cookies) { foreach ($cookies as $cookie) { @@ -247,56 +255,15 @@ public function getCookies($format = self::COOKIES_FLAT) */ public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true) { - $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly)); + $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null)); } /** - * Generates a HTTP Content-Disposition field-value. - * - * @param string $disposition One of "inline" or "attachment" - * @param string $filename A unicode string - * @param string $filenameFallback A string containing only ASCII characters that - * is semantically equivalent to $filename. If the filename is already ASCII, - * it can be omitted, or just copied from $filename - * - * @return string A string suitable for use as a Content-Disposition field-value - * - * @throws \InvalidArgumentException - * - * @see RFC 6266 + * @see HeaderUtils::makeDisposition() */ public function makeDisposition($disposition, $filename, $filenameFallback = '') { - if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) { - throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); - } - - if ('' == $filenameFallback) { - $filenameFallback = $filename; - } - - // filenameFallback is not ASCII. - if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) { - throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); - } - - // percent characters aren't safe in fallback. - if (false !== strpos($filenameFallback, '%')) { - throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); - } - - // path separators aren't allowed in either. - if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) { - throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); - } - - $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback)); - - if ($filename !== $filenameFallback) { - $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename)); - } - - return $output; + return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback); } /** @@ -309,13 +276,13 @@ public function makeDisposition($disposition, $filename, $filenameFallback = '') */ protected function computeCacheControlValue() { - if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { - return 'no-cache, private'; - } - if (!$this->cacheControl) { + if ($this->has('Last-Modified') || $this->has('Expires')) { + return 'private, must-revalidate'; // allows for heuristic expiration (RFC 7234 Section 4.2.2) in the case of "Last-Modified" + } + // conservative by default - return 'private, must-revalidate'; + return 'no-cache, private'; } $header = $this->getCacheControlHeader(); @@ -331,7 +298,7 @@ protected function computeCacheControlValue() return $header; } - private function initDate() + private function initDate(): void { $now = \DateTime::createFromFormat('U', time()); $now->setTimezone(new \DateTimeZone('UTC')); diff --git a/vendor/symfony/http-foundation/ServerBag.php b/vendor/symfony/http-foundation/ServerBag.php index d8ab561..02c7091 100644 --- a/vendor/symfony/http-foundation/ServerBag.php +++ b/vendor/symfony/http-foundation/ServerBag.php @@ -27,14 +27,11 @@ class ServerBag extends ParameterBag */ public function getHeaders() { - $headers = array(); - $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true); + $headers = []; foreach ($this->parameters as $key => $value) { if (0 === strpos($key, 'HTTP_')) { $headers[substr($key, 5)] = $value; - } - // CONTENT_* are not prefixed with HTTP_ - elseif (isset($contentHeaders[$key])) { + } elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) { $headers[$key] = $value; } } @@ -46,13 +43,13 @@ public function getHeaders() /* * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default * For this workaround to work, add these lines to your .htaccess file: - * RewriteCond %{HTTP:Authorization} ^(.+)$ - * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{HTTP:Authorization} .+ + * RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0] * * A sample .htaccess file: * RewriteEngine On - * RewriteCond %{HTTP:Authorization} ^(.+)$ - * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{HTTP:Authorization} .+ + * RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0] * RewriteCond %{REQUEST_FILENAME} !-f * RewriteRule ^(.*)$ app.php [QSA,L] */ @@ -79,7 +76,7 @@ public function getHeaders() /* * XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables, * I'll just set $headers['AUTHORIZATION'] here. - * http://php.net/manual/en/reserved.variables.server.php + * https://php.net/reserved.variables.server */ $headers['AUTHORIZATION'] = $authorizationHeader; } diff --git a/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php b/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php index fc5fb14..ee33698 100644 --- a/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php +++ b/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php @@ -19,12 +19,12 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta private $name = 'attributes'; private $storageKey; - protected $attributes = array(); + protected $attributes = []; /** * @param string $storageKey The key used to store attributes in the session */ - public function __construct($storageKey = '_sf2_attributes') + public function __construct(string $storageKey = '_sf2_attributes') { $this->storageKey = $storageKey; } @@ -63,7 +63,7 @@ public function getStorageKey() */ public function has($name) { - return array_key_exists($name, $this->attributes); + return \array_key_exists($name, $this->attributes); } /** @@ -71,7 +71,7 @@ public function has($name) */ public function get($name, $default = null) { - return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; } /** @@ -95,7 +95,7 @@ public function all() */ public function replace(array $attributes) { - $this->attributes = array(); + $this->attributes = []; foreach ($attributes as $key => $value) { $this->set($key, $value); } @@ -107,7 +107,7 @@ public function replace(array $attributes) public function remove($name) { $retval = null; - if (array_key_exists($name, $this->attributes)) { + if (\array_key_exists($name, $this->attributes)) { $retval = $this->attributes[$name]; unset($this->attributes[$name]); } @@ -121,7 +121,7 @@ public function remove($name) public function clear() { $return = $this->attributes; - $this->attributes = array(); + $this->attributes = []; return $return; } diff --git a/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php index 0d8d179..6fa2293 100644 --- a/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php +++ b/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php @@ -50,15 +50,10 @@ public function set($name, $value); /** * Returns attributes. * - * @return array Attributes + * @return array */ public function all(); - /** - * Sets attributes. - * - * @param array $attributes Attributes - */ public function replace(array $attributes); /** diff --git a/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php index 2f1e01a..2cf0743 100644 --- a/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php +++ b/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php @@ -25,7 +25,7 @@ class NamespacedAttributeBag extends AttributeBag * @param string $storageKey Session storage key * @param string $namespaceCharacter Namespace character to use in keys */ - public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/') + public function __construct(string $storageKey = '_sf2_attributes', string $namespaceCharacter = '/') { $this->namespaceCharacter = $namespaceCharacter; parent::__construct($storageKey); @@ -44,7 +44,7 @@ public function has($name) return false; } - return array_key_exists($name, $attributes); + return \array_key_exists($name, $attributes); } /** @@ -60,7 +60,7 @@ public function get($name, $default = null) return $default; } - return array_key_exists($name, $attributes) ? $attributes[$name] : $default; + return \array_key_exists($name, $attributes) ? $attributes[$name] : $default; } /** @@ -81,7 +81,7 @@ public function remove($name) $retval = null; $attributes = &$this->resolveAttributePath($name); $name = $this->resolveKey($name); - if (null !== $attributes && array_key_exists($name, $attributes)) { + if (null !== $attributes && \array_key_exists($name, $attributes)) { $retval = $attributes[$name]; unset($attributes[$name]); } @@ -97,7 +97,7 @@ public function remove($name) * @param string $name Key name * @param bool $writeContext Write context, default false * - * @return array + * @return array|null */ protected function &resolveAttributePath($name, $writeContext = false) { @@ -115,7 +115,7 @@ protected function &resolveAttributePath($name, $writeContext = false) return $array; } - $array[$parts[0]] = array(); + $array[$parts[0]] = []; return $array; } @@ -123,14 +123,14 @@ protected function &resolveAttributePath($name, $writeContext = false) unset($parts[\count($parts) - 1]); foreach ($parts as $part) { - if (null !== $array && !array_key_exists($part, $array)) { + if (null !== $array && !\array_key_exists($part, $array)) { if (!$writeContext) { $null = null; return $null; } - $array[$part] = array(); + $array[$part] = []; } $array = &$array[$part]; diff --git a/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php index 77521c2..6502f3d 100644 --- a/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php +++ b/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php @@ -19,13 +19,13 @@ class AutoExpireFlashBag implements FlashBagInterface { private $name = 'flashes'; - private $flashes = array('display' => array(), 'new' => array()); + private $flashes = ['display' => [], 'new' => []]; private $storageKey; /** * @param string $storageKey The key used to store flashes in the session */ - public function __construct($storageKey = '_symfony_flashes') + public function __construct(string $storageKey = '_symfony_flashes') { $this->storageKey = $storageKey; } @@ -53,8 +53,8 @@ public function initialize(array &$flashes) // The logic: messages from the last request will be stored in new, so we move them to previous // This request we will show what is in 'display'. What is placed into 'new' this time round will // be moved to display next time round. - $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array(); - $this->flashes['new'] = array(); + $this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : []; + $this->flashes['new'] = []; } /** @@ -68,7 +68,7 @@ public function add($type, $message) /** * {@inheritdoc} */ - public function peek($type, array $default = array()) + public function peek($type, array $default = []) { return $this->has($type) ? $this->flashes['display'][$type] : $default; } @@ -78,13 +78,13 @@ public function peek($type, array $default = array()) */ public function peekAll() { - return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array(); + return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : []; } /** * {@inheritdoc} */ - public function get($type, array $default = array()) + public function get($type, array $default = []) { $return = $default; @@ -106,7 +106,7 @@ public function get($type, array $default = array()) public function all() { $return = $this->flashes['display']; - $this->flashes['display'] = array(); + $this->flashes['display'] = []; return $return; } @@ -132,7 +132,7 @@ public function set($type, $messages) */ public function has($type) { - return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type]; + return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type]; } /** diff --git a/vendor/symfony/http-foundation/Session/Flash/FlashBag.php b/vendor/symfony/http-foundation/Session/Flash/FlashBag.php index 12fb740..c6b7ce3 100644 --- a/vendor/symfony/http-foundation/Session/Flash/FlashBag.php +++ b/vendor/symfony/http-foundation/Session/Flash/FlashBag.php @@ -19,13 +19,13 @@ class FlashBag implements FlashBagInterface { private $name = 'flashes'; - private $flashes = array(); + private $flashes = []; private $storageKey; /** * @param string $storageKey The key used to store flashes in the session */ - public function __construct($storageKey = '_symfony_flashes') + public function __construct(string $storageKey = '_symfony_flashes') { $this->storageKey = $storageKey; } @@ -62,7 +62,7 @@ public function add($type, $message) /** * {@inheritdoc} */ - public function peek($type, array $default = array()) + public function peek($type, array $default = []) { return $this->has($type) ? $this->flashes[$type] : $default; } @@ -78,7 +78,7 @@ public function peekAll() /** * {@inheritdoc} */ - public function get($type, array $default = array()) + public function get($type, array $default = []) { if (!$this->has($type)) { return $default; @@ -97,7 +97,7 @@ public function get($type, array $default = array()) public function all() { $return = $this->peekAll(); - $this->flashes = array(); + $this->flashes = []; return $return; } @@ -123,7 +123,7 @@ public function setAll(array $messages) */ public function has($type) { - return array_key_exists($type, $this->flashes) && $this->flashes[$type]; + return \array_key_exists($type, $this->flashes) && $this->flashes[$type]; } /** diff --git a/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php index f53c9da..99e8074 100644 --- a/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php +++ b/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php @@ -21,7 +21,7 @@ interface FlashBagInterface extends SessionBagInterface { /** - * Adds a flash message for type. + * Adds a flash message for the given type. * * @param string $type * @param mixed $message @@ -29,12 +29,12 @@ interface FlashBagInterface extends SessionBagInterface public function add($type, $message); /** - * Registers a message for a given type. + * Registers one or more messages for a given type. * * @param string $type - * @param string|array $message + * @param string|array $messages */ - public function set($type, $message); + public function set($type, $messages); /** * Gets flash messages for a given type. @@ -44,7 +44,7 @@ public function set($type, $message); * * @return array */ - public function peek($type, array $default = array()); + public function peek($type, array $default = []); /** * Gets all flash messages. @@ -61,7 +61,7 @@ public function peekAll(); * * @return array */ - public function get($type, array $default = array()); + public function get($type, array $default = []); /** * Gets and clears flashes from the stack. diff --git a/vendor/symfony/http-foundation/Session/Session.php b/vendor/symfony/http-foundation/Session/Session.php index 3349906..2192c62 100644 --- a/vendor/symfony/http-foundation/Session/Session.php +++ b/vendor/symfony/http-foundation/Session/Session.php @@ -28,14 +28,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable private $flashName; private $attributeName; - private $data = array(); + private $data = []; private $usageIndex = 0; - /** - * @param SessionStorageInterface $storage A SessionStorageInterface instance - * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) - * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) - */ public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) { $this->storage = $storage ?: new NativeSessionStorage(); @@ -134,29 +129,22 @@ public function getIterator() /** * Returns the number of attributes. * - * @return int The number of attributes + * @return int */ public function count() { return \count($this->getAttributeBag()->all()); } - /** - * @return int - * - * @internal - */ - public function getUsageIndex() + public function &getUsageIndex(): int { return $this->usageIndex; } /** - * @return bool - * * @internal */ - public function isEmpty() + public function isEmpty(): bool { if ($this->isStarted()) { ++$this->usageIndex; @@ -253,7 +241,9 @@ public function registerBag(SessionBagInterface $bag) */ public function getBag($name) { - return $this->storage->getBag($name)->getBag(); + $bag = $this->storage->getBag($name); + + return method_exists($bag, 'getBag') ? $bag->getBag() : $bag; } /** @@ -270,10 +260,8 @@ public function getFlashBag() * Gets the attributebag interface. * * Note that this method was added to help with IDE autocompletion. - * - * @return AttributeBagInterface */ - private function getAttributeBag() + private function getAttributeBag(): AttributeBagInterface { return $this->getBag($this->attributeName); } diff --git a/vendor/symfony/http-foundation/Session/SessionBagProxy.php b/vendor/symfony/http-foundation/Session/SessionBagProxy.php index 3504bdf..0ae8231 100644 --- a/vendor/symfony/http-foundation/Session/SessionBagProxy.php +++ b/vendor/symfony/http-foundation/Session/SessionBagProxy.php @@ -22,27 +22,21 @@ final class SessionBagProxy implements SessionBagInterface private $data; private $usageIndex; - public function __construct(SessionBagInterface $bag, array &$data, &$usageIndex) + public function __construct(SessionBagInterface $bag, array &$data, ?int &$usageIndex) { $this->bag = $bag; $this->data = &$data; $this->usageIndex = &$usageIndex; } - /** - * @return SessionBagInterface - */ - public function getBag() + public function getBag(): SessionBagInterface { ++$this->usageIndex; return $this->bag; } - /** - * @return bool - */ - public function isEmpty() + public function isEmpty(): bool { if (!isset($this->data[$this->bag->getStorageKey()])) { return true; @@ -55,7 +49,7 @@ public function isEmpty() /** * {@inheritdoc} */ - public function getName() + public function getName(): string { return $this->bag->getName(); } @@ -63,7 +57,7 @@ public function getName() /** * {@inheritdoc} */ - public function initialize(array &$array) + public function initialize(array &$array): void { ++$this->usageIndex; $this->data[$this->bag->getStorageKey()] = &$array; @@ -74,7 +68,7 @@ public function initialize(array &$array) /** * {@inheritdoc} */ - public function getStorageKey() + public function getStorageKey(): string { return $this->bag->getStorageKey(); } diff --git a/vendor/symfony/http-foundation/Session/SessionInterface.php b/vendor/symfony/http-foundation/Session/SessionInterface.php index 95fca85..e758c6b 100644 --- a/vendor/symfony/http-foundation/Session/SessionInterface.php +++ b/vendor/symfony/http-foundation/Session/SessionInterface.php @@ -23,7 +23,7 @@ interface SessionInterface /** * Starts the session storage. * - * @return bool True if session started + * @return bool * * @throws \RuntimeException if session fails to start */ @@ -32,7 +32,7 @@ public function start(); /** * Returns the session ID. * - * @return string The session ID + * @return string */ public function getId(); @@ -46,7 +46,7 @@ public function setId($id); /** * Returns the session name. * - * @return mixed The session name + * @return string */ public function getName(); @@ -68,7 +68,7 @@ public function setName($name); * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. * - * @return bool True if session invalidated, false if error + * @return bool */ public function invalidate($lifetime = null); @@ -82,7 +82,7 @@ public function invalidate($lifetime = null); * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. * - * @return bool True if session migrated, false if error + * @return bool */ public function migrate($destroy = false, $lifetime = null); @@ -100,7 +100,7 @@ public function save(); * * @param string $name The attribute name * - * @return bool true if the attribute is defined, false otherwise + * @return bool */ public function has($name); @@ -125,14 +125,12 @@ public function set($name, $value); /** * Returns attributes. * - * @return array Attributes + * @return array */ public function all(); /** * Sets attributes. - * - * @param array $attributes Attributes */ public function replace(array $attributes); diff --git a/vendor/symfony/http-foundation/Session/SessionUtils.php b/vendor/symfony/http-foundation/Session/SessionUtils.php new file mode 100644 index 0000000..b5bce4a --- /dev/null +++ b/vendor/symfony/http-foundation/Session/SessionUtils.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * Session utility functions. + * + * @author Nicolas Grekas + * @author Rémon van de Kamp + * + * @internal + */ +final class SessionUtils +{ + /** + * Finds the session header amongst the headers that are to be sent, removes it, and returns + * it so the caller can process it further. + */ + public static function popSessionCookie(string $sessionName, string $sessionId): ?string + { + $sessionCookie = null; + $sessionCookiePrefix = sprintf(' %s=', urlencode($sessionName)); + $sessionCookieWithId = sprintf('%s%s;', $sessionCookiePrefix, urlencode($sessionId)); + $otherCookies = []; + foreach (headers_list() as $h) { + if (0 !== stripos($h, 'Set-Cookie:')) { + continue; + } + if (11 === strpos($h, $sessionCookiePrefix, 11)) { + $sessionCookie = $h; + + if (11 !== strpos($h, $sessionCookieWithId, 11)) { + $otherCookies[] = $h; + } + } else { + $otherCookies[] = $h; + } + } + if (null === $sessionCookie) { + return null; + } + + header_remove('Set-Cookie'); + foreach ($otherCookies as $h) { + header($h, false); + } + + return $sessionCookie; + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php index 0d11949..a196833 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php @@ -11,6 +11,8 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; +use Symfony\Component\HttpFoundation\Session\SessionUtils; + /** * This abstract session handler provides a generic implementation * of the PHP 7.0 SessionUpdateTimestampHandlerInterface, @@ -27,7 +29,7 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess private $igbinaryEmptyData; /** - * {@inheritdoc} + * @return bool */ public function open($savePath, $sessionName) { @@ -62,7 +64,7 @@ abstract protected function doWrite($sessionId, $data); abstract protected function doDestroy($sessionId); /** - * {@inheritdoc} + * @return bool */ public function validateId($sessionId) { @@ -73,7 +75,7 @@ public function validateId($sessionId) } /** - * {@inheritdoc} + * @return string */ public function read($sessionId) { @@ -91,29 +93,18 @@ public function read($sessionId) $data = $this->doRead($sessionId); $this->newSessionId = '' === $data ? $sessionId : null; - if (\PHP_VERSION_ID < 70000) { - $this->prefetchData = $data; - } return $data; } /** - * {@inheritdoc} + * @return bool */ public function write($sessionId, $data) { - if (\PHP_VERSION_ID < 70000 && $this->prefetchData) { - $readData = $this->prefetchData; - $this->prefetchData = null; - - if ($readData === $data) { - return $this->updateTimestamp($sessionId, $data); - } - } if (null === $this->igbinaryEmptyData) { // see https://github.com/igbinary/igbinary/issues/146 - $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : ''; + $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize([]) : ''; } if ('' === $data || $this->igbinaryEmptyData === $data) { return $this->destroy($sessionId); @@ -124,42 +115,31 @@ public function write($sessionId, $data) } /** - * {@inheritdoc} + * @return bool */ public function destroy($sessionId) { - if (\PHP_VERSION_ID < 70000) { - $this->prefetchData = null; - } if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) { if (!$this->sessionName) { - throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', \get_class($this))); + throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', static::class)); } - $sessionCookie = sprintf(' %s=', urlencode($this->sessionName)); - $sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId)); - $sessionCookieFound = false; - $otherCookies = array(); - foreach (headers_list() as $h) { - if (0 !== stripos($h, 'Set-Cookie:')) { - continue; - } - if (11 === strpos($h, $sessionCookie, 11)) { - $sessionCookieFound = true; - - if (11 !== strpos($h, $sessionCookieWithId, 11)) { - $otherCookies[] = $h; - } + $cookie = SessionUtils::popSessionCookie($this->sessionName, $sessionId); + + /* + * We send an invalidation Set-Cookie header (zero lifetime) + * when either the session was started or a cookie with + * the session name was sent by the client (in which case + * we know it's invalid as a valid session cookie would've + * started the session). + */ + if (null === $cookie || isset($_COOKIE[$this->sessionName])) { + if (\PHP_VERSION_ID < 70300) { + setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN)); } else { - $otherCookies[] = $h; - } - } - if ($sessionCookieFound) { - header_remove('Set-Cookie'); - foreach ($otherCookies as $h) { - header($h, false); + $params = session_get_cookie_params(); + unset($params['lifetime']); + setcookie($this->sessionName, '', $params); } - } else { - setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN)); } } diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php deleted file mode 100644 index 1b12c92..0000000 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php +++ /dev/null @@ -1,118 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; - -@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED); - -/** - * @author Drak - * - * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead. - */ -class MemcacheSessionHandler implements \SessionHandlerInterface -{ - private $memcache; - - /** - * @var int Time to live in seconds - */ - private $ttl; - - /** - * @var string Key prefix for shared environments - */ - private $prefix; - - /** - * Constructor. - * - * List of available options: - * * prefix: The prefix to use for the memcache keys in order to avoid collision - * * expiretime: The time to live in seconds - * - * @param \Memcache $memcache A \Memcache instance - * @param array $options An associative array of Memcache options - * - * @throws \InvalidArgumentException When unsupported options are passed - */ - public function __construct(\Memcache $memcache, array $options = array()) - { - if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { - throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff))); - } - - $this->memcache = $memcache; - $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; - $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; - } - - /** - * {@inheritdoc} - */ - public function open($savePath, $sessionName) - { - return true; - } - - /** - * {@inheritdoc} - */ - public function close() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function read($sessionId) - { - return $this->memcache->get($this->prefix.$sessionId) ?: ''; - } - - /** - * {@inheritdoc} - */ - public function write($sessionId, $data) - { - return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl); - } - - /** - * {@inheritdoc} - */ - public function destroy($sessionId) - { - $this->memcache->delete($this->prefix.$sessionId); - - return true; - } - - /** - * {@inheritdoc} - */ - public function gc($maxlifetime) - { - // not required here because memcache will auto expire the records anyhow. - return true; - } - - /** - * Return a Memcache instance. - * - * @return \Memcache - */ - protected function getMemcache() - { - return $this->memcache; - } -} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php index 61a7afd..a399be5 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -15,7 +15,7 @@ * Memcached based session storage handler based on the Memcached class * provided by the PHP memcached extension. * - * @see http://php.net/memcached + * @see https://php.net/memcached * * @author Drak */ @@ -40,16 +40,13 @@ class MemcachedSessionHandler extends AbstractSessionHandler * * prefix: The prefix to use for the memcached keys in order to avoid collision * * expiretime: The time to live in seconds. * - * @param \Memcached $memcached A \Memcached instance - * @param array $options An associative array of Memcached options - * * @throws \InvalidArgumentException When unsupported options are passed */ - public function __construct(\Memcached $memcached, array $options = array()) + public function __construct(\Memcached $memcached, array $options = []) { $this->memcached = $memcached; - if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { + if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) { throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff))); } @@ -58,11 +55,11 @@ public function __construct(\Memcached $memcached, array $options = array()) } /** - * {@inheritdoc} + * @return bool */ public function close() { - return true; + return $this->memcached->quit(); } /** @@ -74,7 +71,7 @@ protected function doRead($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function updateTimestamp($sessionId, $data) { @@ -102,7 +99,7 @@ protected function doDestroy($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function gc($maxlifetime) { diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php new file mode 100644 index 0000000..c6b16d1 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Migrating session handler for migrating from one handler to another. It reads + * from the current handler and writes both the current and new ones. + * + * It ignores errors from the new handler. + * + * @author Ross Motley + * @author Oliver Radwell + */ +class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface +{ + private $currentHandler; + private $writeOnlyHandler; + + public function __construct(\SessionHandlerInterface $currentHandler, \SessionHandlerInterface $writeOnlyHandler) + { + if (!$currentHandler instanceof \SessionUpdateTimestampHandlerInterface) { + $currentHandler = new StrictSessionHandler($currentHandler); + } + if (!$writeOnlyHandler instanceof \SessionUpdateTimestampHandlerInterface) { + $writeOnlyHandler = new StrictSessionHandler($writeOnlyHandler); + } + + $this->currentHandler = $currentHandler; + $this->writeOnlyHandler = $writeOnlyHandler; + } + + /** + * @return bool + */ + public function close() + { + $result = $this->currentHandler->close(); + $this->writeOnlyHandler->close(); + + return $result; + } + + /** + * @return bool + */ + public function destroy($sessionId) + { + $result = $this->currentHandler->destroy($sessionId); + $this->writeOnlyHandler->destroy($sessionId); + + return $result; + } + + /** + * @return bool + */ + public function gc($maxlifetime) + { + $result = $this->currentHandler->gc($maxlifetime); + $this->writeOnlyHandler->gc($maxlifetime); + + return $result; + } + + /** + * @return bool + */ + public function open($savePath, $sessionName) + { + $result = $this->currentHandler->open($savePath, $sessionName); + $this->writeOnlyHandler->open($savePath, $sessionName); + + return $result; + } + + /** + * @return string + */ + public function read($sessionId) + { + // No reading from new handler until switch-over + return $this->currentHandler->read($sessionId); + } + + /** + * @return bool + */ + public function write($sessionId, $sessionData) + { + $result = $this->currentHandler->write($sessionId, $sessionData); + $this->writeOnlyHandler->write($sessionId, $sessionData); + + return $result; + } + + /** + * @return bool + */ + public function validateId($sessionId) + { + // No reading from new handler until switch-over + return $this->currentHandler->validateId($sessionId); + } + + /** + * @return bool + */ + public function updateTimestamp($sessionId, $sessionData) + { + $result = $this->currentHandler->updateTimestamp($sessionId, $sessionData); + $this->writeOnlyHandler->updateTimestamp($sessionId, $sessionData); + + return $result; + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php index 7d3fa21..27e0800 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -17,14 +17,14 @@ * @author Markus Bachmann * * @see https://packagist.org/packages/mongodb/mongodb - * @see http://php.net/manual/en/set.mongodb.php + * @see https://php.net/mongodb */ class MongoDbSessionHandler extends AbstractSessionHandler { private $mongo; /** - * @var \MongoCollection + * @var \MongoDB\Collection */ private $collection; @@ -56,43 +56,31 @@ class MongoDbSessionHandler extends AbstractSessionHandler * { "expireAfterSeconds": 0 } * ) * - * More details on: http://docs.mongodb.org/manual/tutorial/expire-data/ + * More details on: https://docs.mongodb.org/manual/tutorial/expire-data/ * * If you use such an index, you can drop `gc_probability` to 0 since * no garbage-collection is required. * - * @param \MongoDB\Client $mongo A MongoDB\Client instance - * @param array $options An associative array of field options - * - * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided * @throws \InvalidArgumentException When "database" or "collection" not provided */ - public function __construct($mongo, array $options) + public function __construct(\MongoDB\Client $mongo, array $options) { - if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) { - @trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED); - } - - if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { - throw new \InvalidArgumentException('MongoClient or Mongo instance required'); - } - if (!isset($options['database']) || !isset($options['collection'])) { throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler'); } $this->mongo = $mongo; - $this->options = array_merge(array( + $this->options = array_merge([ 'id_field' => '_id', 'data_field' => 'data', 'time_field' => 'time', 'expiry_field' => 'expires_at', - ), $options); + ], $options); } /** - * {@inheritdoc} + * @return bool */ public function close() { @@ -104,25 +92,21 @@ public function close() */ protected function doDestroy($sessionId) { - $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove'; - - $this->getCollection()->$methodName(array( + $this->getCollection()->deleteOne([ $this->options['id_field'] => $sessionId, - )); + ]); return true; } /** - * {@inheritdoc} + * @return bool */ public function gc($maxlifetime) { - $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove'; - - $this->getCollection()->$methodName(array( - $this->options['expiry_field'] => array('$lt' => $this->createDateTime()), - )); + $this->getCollection()->deleteMany([ + $this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()], + ]); return true; } @@ -132,55 +116,36 @@ public function gc($maxlifetime) */ protected function doWrite($sessionId, $data) { - $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); + $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); - $fields = array( - $this->options['time_field'] => $this->createDateTime(), + $fields = [ + $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), $this->options['expiry_field'] => $expiry, - ); - - $options = array('upsert' => true); - - if ($this->mongo instanceof \MongoDB\Client) { - $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY); - } else { - $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY); - $options['multiple'] = false; - } + $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY), + ]; - $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update'; - - $this->getCollection()->$methodName( - array($this->options['id_field'] => $sessionId), - array('$set' => $fields), - $options + $this->getCollection()->updateOne( + [$this->options['id_field'] => $sessionId], + ['$set' => $fields], + ['upsert' => true] ); return true; } /** - * {@inheritdoc} + * @return bool */ public function updateTimestamp($sessionId, $data) { - $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); - - if ($this->mongo instanceof \MongoDB\Client) { - $methodName = 'updateOne'; - $options = array(); - } else { - $methodName = 'update'; - $options = array('multiple' => false); - } + $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); - $this->getCollection()->$methodName( - array($this->options['id_field'] => $sessionId), - array('$set' => array( - $this->options['time_field'] => $this->createDateTime(), + $this->getCollection()->updateOne( + [$this->options['id_field'] => $sessionId], + ['$set' => [ + $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), $this->options['expiry_field'] => $expiry, - )), - $options + ]] ); return true; @@ -191,28 +156,19 @@ public function updateTimestamp($sessionId, $data) */ protected function doRead($sessionId) { - $dbData = $this->getCollection()->findOne(array( + $dbData = $this->getCollection()->findOne([ $this->options['id_field'] => $sessionId, - $this->options['expiry_field'] => array('$gte' => $this->createDateTime()), - )); + $this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()], + ]); if (null === $dbData) { return ''; } - if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) { - return $dbData[$this->options['data_field']]->getData(); - } - - return $dbData[$this->options['data_field']]->bin; + return $dbData[$this->options['data_field']]->getData(); } - /** - * Return a "MongoCollection" instance. - * - * @return \MongoCollection - */ - private function getCollection() + private function getCollection(): \MongoDB\Collection { if (null === $this->collection) { $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']); @@ -222,34 +178,10 @@ private function getCollection() } /** - * Return a Mongo instance. - * - * @return \Mongo|\MongoClient|\MongoDB\Client + * @return \MongoDB\Client */ protected function getMongo() { return $this->mongo; } - - /** - * Create a date object using the class appropriate for the current mongo connection. - * - * Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime - * - * @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now. - * - * @return \MongoDate|\MongoDB\BSON\UTCDateTime - */ - private function createDateTime($seconds = null) - { - if (null === $seconds) { - $seconds = time(); - } - - if ($this->mongo instanceof \MongoDB\Client) { - return new \MongoDB\BSON\UTCDateTime($seconds * 1000); - } - - return new \MongoDate($seconds); - } } diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php index 4e9704b..bdfc9d8 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php @@ -16,19 +16,19 @@ * * @author Drak */ -class NativeFileSessionHandler extends NativeSessionHandler +class NativeFileSessionHandler extends \SessionHandler { /** * @param string $savePath Path of directory to save session files * Default null will leave setting as defined by PHP. * '/path', 'N;/path', or 'N;octal-mode;/path * - * @see http://php.net/session.configuration.php#ini.session.save-path for further details. + * @see https://php.net/session.configuration#ini.session.save-path for further details. * * @throws \InvalidArgumentException On invalid $savePath * @throws \RuntimeException When failing to create the save directory */ - public function __construct($savePath = null) + public function __construct(string $savePath = null) { if (null === $savePath) { $savePath = ini_get('session.save_path'); diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php deleted file mode 100644 index 9be4528..0000000 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; - -/** - * @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead. - * @see http://php.net/sessionhandler - */ -class NativeSessionHandler extends \SessionHandler -{ - public function __construct() - { - @trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED); - } -} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php index 8d19315..0634e46 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php @@ -19,7 +19,7 @@ class NullSessionHandler extends AbstractSessionHandler { /** - * {@inheritdoc} + * @return bool */ public function close() { @@ -27,7 +27,7 @@ public function close() } /** - * {@inheritdoc} + * @return bool */ public function validateId($sessionId) { @@ -43,7 +43,7 @@ protected function doRead($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function updateTimestamp($sessionId, $data) { @@ -67,7 +67,7 @@ protected function doDestroy($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function gc($maxlifetime) { diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php index 8c0c42f..034c02e 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php @@ -32,7 +32,7 @@ * Saving it in a character column could corrupt the data. You can use createTable() * to initialize a correctly defined table. * - * @see http://php.net/sessionhandlerinterface + * @see https://php.net/sessionhandlerinterface * * @author Fabien Potencier * @author Michael Williams @@ -65,6 +65,8 @@ class PdoSessionHandler extends AbstractSessionHandler */ const LOCK_TRANSACTIONAL = 2; + private const MAX_LIFETIME = 315576000; + /** * @var \PDO|null PDO instance or null when not connected yet */ @@ -118,7 +120,7 @@ class PdoSessionHandler extends AbstractSessionHandler /** * @var array Connection options when lazy-connect */ - private $connectionOptions = array(); + private $connectionOptions = []; /** * @var int The strategy for locking, see constants @@ -130,7 +132,7 @@ class PdoSessionHandler extends AbstractSessionHandler * * @var \PDOStatement[] An array of statements to release advisory locks */ - private $unlockStatements = array(); + private $unlockStatements = []; /** * @var bool True when the current session exists but expired according to session.gc_maxlifetime @@ -161,15 +163,14 @@ class PdoSessionHandler extends AbstractSessionHandler * * db_time_col: The column where to store the timestamp [default: sess_time] * * db_username: The username when lazy-connect [default: ''] * * db_password: The password when lazy-connect [default: ''] - * * db_connection_options: An array of driver-specific connection options [default: array()] + * * db_connection_options: An array of driver-specific connection options [default: []] * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL] * * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null - * @param array $options An associative array of options * * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION */ - public function __construct($pdoOrDsn = null, array $options = array()) + public function __construct($pdoOrDsn = null, array $options = []) { if ($pdoOrDsn instanceof \PDO) { if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { @@ -218,7 +219,7 @@ public function createTable() // - trailing space removal // - case-insensitivity // - language processing like é == e - $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB"; + $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB"; break; case 'sqlite': $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; @@ -238,6 +239,7 @@ public function createTable() try { $this->pdo->exec($sql); + $this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)"); } catch (\PDOException $e) { $this->rollback(); @@ -258,7 +260,7 @@ public function isSessionExpired() } /** - * {@inheritdoc} + * @return bool */ public function open($savePath, $sessionName) { @@ -272,7 +274,7 @@ public function open($savePath, $sessionName) } /** - * {@inheritdoc} + * @return string */ public function read($sessionId) { @@ -286,7 +288,7 @@ public function read($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function gc($maxlifetime) { @@ -365,18 +367,18 @@ protected function doWrite($sessionId, $data) } /** - * {@inheritdoc} + * @return bool */ public function updateTimestamp($sessionId, $data) { - $maxlifetime = (int) ini_get('session.gc_maxlifetime'); + $expiry = time() + (int) ini_get('session.gc_maxlifetime'); try { $updateStmt = $this->pdo->prepare( - "UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id" + "UPDATE $this->table SET $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id" ); $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $updateStmt->bindParam(':expiry', $expiry, \PDO::PARAM_INT); $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); $updateStmt->execute(); } catch (\PDOException $e) { @@ -389,7 +391,7 @@ public function updateTimestamp($sessionId, $data) } /** - * {@inheritdoc} + * @return bool */ public function close() { @@ -403,14 +405,21 @@ public function close() $this->gcCalled = false; // delete the session records that have expired + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time AND $this->lifetimeCol > :min"; + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT); + $stmt->execute(); + // to be removed in 6.0 if ('mysql' === $this->driver) { - $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time"; + $legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol + $this->timeCol < :time"; } else { - $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol"; + $legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol < :time - $this->timeCol"; } - $stmt = $this->pdo->prepare($sql); + $stmt = $this->pdo->prepare($legacySql); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT); $stmt->execute(); } @@ -423,10 +432,8 @@ public function close() /** * Lazy-connects to the database. - * - * @param string $dsn DSN string */ - private function connect($dsn) + private function connect(string $dsn): void { $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions); $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); @@ -436,13 +443,9 @@ private function connect($dsn) /** * Builds a PDO DSN from a URL-like connection string. * - * @param string $dsnOrUrl - * - * @return string - * * @todo implement missing support for oci DSN (which look totally different from other PDO ones) */ - private function buildDsnFromUrl($dsnOrUrl) + private function buildDsnFromUrl(string $dsnOrUrl): string { // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl); @@ -468,13 +471,13 @@ private function buildDsnFromUrl($dsnOrUrl) throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler'); } - $driverAliasMap = array( + $driverAliasMap = [ 'mssql' => 'sqlsrv', 'mysql2' => 'mysql', // Amazon RDS, for some weird reason 'postgres' => 'pgsql', 'postgresql' => 'pgsql', 'sqlite3' => 'sqlite', - ); + ]; $driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme']; @@ -538,10 +541,10 @@ private function buildDsnFromUrl($dsnOrUrl) * PDO::rollback or PDO::inTransaction for SQLite. * * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions - * due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ . + * due to https://percona.com/blog/2013/12/12/one-more-innodb-gap-lock-to-avoid/ . * So we change it to READ COMMITTED. */ - private function beginTransaction() + private function beginTransaction(): void { if (!$this->inTransaction) { if ('sqlite' === $this->driver) { @@ -559,7 +562,7 @@ private function beginTransaction() /** * Helper method to commit a transaction. */ - private function commit() + private function commit(): void { if ($this->inTransaction) { try { @@ -581,7 +584,7 @@ private function commit() /** * Helper method to rollback a transaction. */ - private function rollback() + private function rollback(): void { // We only need to rollback if we are in a transaction. Otherwise the resulting // error would hide the real problem why rollback was called. We might not be @@ -623,7 +626,12 @@ protected function doRead($sessionId) $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); if ($sessionRows) { - if ($sessionRows[0][1] + $sessionRows[0][2] < time()) { + $expiry = (int) $sessionRows[0][1]; + if ($expiry <= self::MAX_LIFETIME) { + $expiry += $sessionRows[0][2]; + } + + if ($expiry < time()) { $this->sessionExpired = true; return ''; @@ -668,8 +676,6 @@ protected function doRead($sessionId) /** * Executes an application-level lock on the database. * - * @param string $sessionId Session ID - * * @return \PDOStatement The statement that needs to be executed later to release the lock * * @throws \DomainException When an unsupported PDO driver is used @@ -678,12 +684,12 @@ protected function doRead($sessionId) * - for oci using DBMS_LOCK.REQUEST * - for sqlsrv using sp_getapplock with LockOwner = Session */ - private function doAdvisoryLock($sessionId) + private function doAdvisoryLock(string $sessionId): \PDOStatement { switch ($this->driver) { case 'mysql': // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced. - $lockId = \substr($sessionId, 0, 64); + $lockId = substr($sessionId, 0, 64); // should we handle the return value? 0 on timeout, null on error // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)'); @@ -733,12 +739,8 @@ private function doAdvisoryLock($sessionId) * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer. * * Keep in mind, PHP integers are signed. - * - * @param string $string - * - * @return int */ - private function convertStringToInt($string) + private function convertStringToInt(string $string): int { if (4 === \PHP_INT_SIZE) { return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]); @@ -753,15 +755,14 @@ private function convertStringToInt($string) /** * Return a locking or nonlocking SQL query to read session information. * - * @return string The SQL string - * * @throws \DomainException When an unsupported PDO driver is used */ - private function getSelectSql() + private function getSelectSql(): string { if (self::LOCK_TRANSACTIONAL === $this->lockMode) { $this->beginTransaction(); + // selecting the time column should be removed in 6.0 switch ($this->driver) { case 'mysql': case 'oci': @@ -782,32 +783,26 @@ private function getSelectSql() /** * Returns an insert statement supported by the database for writing session data. - * - * @param string $sessionId Session ID - * @param string $sessionData Encoded session data - * @param int $maxlifetime session.gc_maxlifetime - * - * @return \PDOStatement The insert statement */ - private function getInsertStatement($sessionId, $sessionData, $maxlifetime) + private function getInsertStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement { switch ($this->driver) { case 'oci': $data = fopen('php://memory', 'r+'); fwrite($data, $sessionData); rewind($data); - $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $this->dataCol into :data"; + $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :expiry, :time) RETURNING $this->dataCol into :data"; break; default: $data = $sessionData; - $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; + $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)"; break; } $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); - $stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); return $stmt; @@ -815,32 +810,26 @@ private function getInsertStatement($sessionId, $sessionData, $maxlifetime) /** * Returns an update statement supported by the database for writing session data. - * - * @param string $sessionId Session ID - * @param string $sessionData Encoded session data - * @param int $maxlifetime session.gc_maxlifetime - * - * @return \PDOStatement The update statement */ - private function getUpdateStatement($sessionId, $sessionData, $maxlifetime) + private function getUpdateStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement { switch ($this->driver) { case 'oci': $data = fopen('php://memory', 'r+'); fwrite($data, $sessionData); rewind($data); - $sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data"; + $sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data"; break; default: $data = $sessionData; - $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"; + $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id"; break; } $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); - $stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); return $stmt; @@ -848,36 +837,30 @@ private function getUpdateStatement($sessionId, $sessionData, $maxlifetime) /** * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data. - * - * @param string $sessionId Session ID - * @param string $data Encoded session data - * @param int $maxlifetime session.gc_maxlifetime - * - * @return \PDOStatement|null The merge statement or null when not supported */ - private function getMergeStatement($sessionId, $data, $maxlifetime) + private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement { switch (true) { case 'mysql' === $this->driver: - $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ". "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; break; case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): // MERGE is only available since SQL Server 2008 and must be terminated by semicolon - // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx + // It also requires HOLDLOCK according to https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/ $mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ". "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ". "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;"; break; case 'sqlite' === $this->driver: - $mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; + $mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)"; break; case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='): - $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ". "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; break; default: - // MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html + // MERGE is not supported with LOBs: https://oracle.com/technetwork/articles/fuecks-lobs-095315.html return null; } @@ -887,15 +870,15 @@ private function getMergeStatement($sessionId, $data, $maxlifetime) $mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB); - $mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT); + $mergeStmt->bindValue(4, time() + $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(5, time(), \PDO::PARAM_INT); $mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB); - $mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT); + $mergeStmt->bindValue(7, time() + $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(8, time(), \PDO::PARAM_INT); } else { $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB); - $mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $mergeStmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); } diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php new file mode 100644 index 0000000..e5be90d --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +use Predis\Response\ErrorInterface; +use Symfony\Component\Cache\Traits\RedisClusterProxy; +use Symfony\Component\Cache\Traits\RedisProxy; + +/** + * Redis based session storage handler based on the Redis class + * provided by the PHP redis extension. + * + * @author Dalibor Karlović + */ +class RedisSessionHandler extends AbstractSessionHandler +{ + private $redis; + + /** + * @var string Key prefix for shared environments + */ + private $prefix; + + /** + * @var int Time to live in seconds + */ + private $ttl; + + /** + * List of available options: + * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server + * * ttl: The time to live in seconds. + * + * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis + * + * @throws \InvalidArgumentException When unsupported client or options are passed + */ + public function __construct($redis, array $options = []) + { + if ( + !$redis instanceof \Redis && + !$redis instanceof \RedisArray && + !$redis instanceof \RedisCluster && + !$redis instanceof \Predis\ClientInterface && + !$redis instanceof RedisProxy && + !$redis instanceof RedisClusterProxy + ) { + throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); + } + + if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) { + throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff))); + } + + $this->redis = $redis; + $this->prefix = $options['prefix'] ?? 'sf_s'; + $this->ttl = $options['ttl'] ?? null; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId): string + { + return $this->redis->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data): bool + { + $result = $this->redis->setEx($this->prefix.$sessionId, (int) ($this->ttl ?? ini_get('session.gc_maxlifetime')), $data); + + return $result && !$result instanceof ErrorInterface; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId): bool + { + $this->redis->del($this->prefix.$sessionId); + + return true; + } + + /** + * {@inheritdoc} + */ + public function close(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime): bool + { + return true; + } + + /** + * @return bool + */ + public function updateTimestamp($sessionId, $data) + { + return (bool) $this->redis->expire($this->prefix.$sessionId, (int) ($this->ttl ?? ini_get('session.gc_maxlifetime'))); + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php b/vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php new file mode 100644 index 0000000..b92bd0f --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +use Doctrine\DBAL\DriverManager; +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Traits\RedisClusterProxy; +use Symfony\Component\Cache\Traits\RedisProxy; + +/** + * @author Nicolas Grekas + */ +class SessionHandlerFactory +{ + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy|\Memcached|\PDO|string $connection Connection or DSN + */ + public static function createHandler($connection): AbstractSessionHandler + { + if (!\is_string($connection) && !\is_object($connection)) { + throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection))); + } + + switch (true) { + case $connection instanceof \Redis: + case $connection instanceof \RedisArray: + case $connection instanceof \RedisCluster: + case $connection instanceof \Predis\ClientInterface: + case $connection instanceof RedisProxy: + case $connection instanceof RedisClusterProxy: + return new RedisSessionHandler($connection); + + case $connection instanceof \Memcached: + return new MemcachedSessionHandler($connection); + + case $connection instanceof \PDO: + return new PdoSessionHandler($connection); + + case !\is_string($connection): + throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection))); + case 0 === strpos($connection, 'file://'): + return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7))); + + case 0 === strpos($connection, 'redis:'): + case 0 === strpos($connection, 'rediss:'): + case 0 === strpos($connection, 'memcached:'): + if (!class_exists(AbstractAdapter::class)) { + throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $connection)); + } + $handlerClass = 0 === strpos($connection, 'memcached:') ? MemcachedSessionHandler::class : RedisSessionHandler::class; + $connection = AbstractAdapter::createConnection($connection, ['lazy' => true]); + + return new $handlerClass($connection); + + case 0 === strpos($connection, 'pdo_oci://'): + if (!class_exists(DriverManager::class)) { + throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection)); + } + $connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection(); + // no break; + + case 0 === strpos($connection, 'mssql://'): + case 0 === strpos($connection, 'mysql://'): + case 0 === strpos($connection, 'mysql2://'): + case 0 === strpos($connection, 'pgsql://'): + case 0 === strpos($connection, 'postgres://'): + case 0 === strpos($connection, 'postgresql://'): + case 0 === strpos($connection, 'sqlsrv://'): + case 0 === strpos($connection, 'sqlite://'): + case 0 === strpos($connection, 'sqlite3://'): + return new PdoSessionHandler($connection); + } + + throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection)); + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php index 83a1f2c..3144ea5 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php @@ -31,7 +31,7 @@ public function __construct(\SessionHandlerInterface $handler) } /** - * {@inheritdoc} + * @return bool */ public function open($savePath, $sessionName) { @@ -49,7 +49,7 @@ protected function doRead($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function updateTimestamp($sessionId, $data) { @@ -65,7 +65,7 @@ protected function doWrite($sessionId, $data) } /** - * {@inheritdoc} + * @return bool */ public function destroy($sessionId) { @@ -86,7 +86,7 @@ protected function doDestroy($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function close() { @@ -94,7 +94,7 @@ public function close() } /** - * {@inheritdoc} + * @return bool */ public function gc($maxlifetime) { diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php deleted file mode 100644 index 127e47f..0000000 --- a/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; - -/** - * Wraps another SessionHandlerInterface to only write the session when it has been modified. - * - * @author Adrien Brault - * - * @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead. - */ -class WriteCheckSessionHandler implements \SessionHandlerInterface -{ - private $wrappedSessionHandler; - - /** - * @var array sessionId => session - */ - private $readSessions; - - public function __construct(\SessionHandlerInterface $wrappedSessionHandler) - { - @trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', self::class), E_USER_DEPRECATED); - - $this->wrappedSessionHandler = $wrappedSessionHandler; - } - - /** - * {@inheritdoc} - */ - public function close() - { - return $this->wrappedSessionHandler->close(); - } - - /** - * {@inheritdoc} - */ - public function destroy($sessionId) - { - return $this->wrappedSessionHandler->destroy($sessionId); - } - - /** - * {@inheritdoc} - */ - public function gc($maxlifetime) - { - return $this->wrappedSessionHandler->gc($maxlifetime); - } - - /** - * {@inheritdoc} - */ - public function open($savePath, $sessionName) - { - return $this->wrappedSessionHandler->open($savePath, $sessionName); - } - - /** - * {@inheritdoc} - */ - public function read($sessionId) - { - $session = $this->wrappedSessionHandler->read($sessionId); - - $this->readSessions[$sessionId] = $session; - - return $session; - } - - /** - * {@inheritdoc} - */ - public function write($sessionId, $data) - { - if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) { - return true; - } - - return $this->wrappedSessionHandler->write($sessionId, $data); - } -} diff --git a/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php b/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php index 6f59af4..5fe40fc 100644 --- a/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php +++ b/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php @@ -39,7 +39,7 @@ class MetadataBag implements SessionBagInterface /** * @var array */ - protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0); + protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0]; /** * Unix timestamp. @@ -57,7 +57,7 @@ class MetadataBag implements SessionBagInterface * @param string $storageKey The key used to store bag in the session * @param int $updateThreshold The time to wait between two UPDATED updates */ - public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0) + public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0) { $this->storageKey = $storageKey; $this->updateThreshold = $updateThreshold; @@ -159,7 +159,7 @@ public function setName($name) $this->name = $name; } - private function stampCreated($lifetime = null) + private function stampCreated(int $lifetime = null): void { $timeStamp = time(); $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp; diff --git a/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php index 027f4ef..37b6f14 100644 --- a/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php +++ b/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php @@ -50,7 +50,7 @@ class MockArraySessionStorage implements SessionStorageInterface /** * @var array */ - protected $data = array(); + protected $data = []; /** * @var MetadataBag @@ -60,13 +60,9 @@ class MockArraySessionStorage implements SessionStorageInterface /** * @var array|SessionBagInterface[] */ - protected $bags = array(); + protected $bags = []; - /** - * @param string $name Session name - * @param MetadataBag $metaBag MetadataBag instance - */ - public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null) + public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null) { $this->name = $name; $this->setMetadataBag($metaBag); @@ -170,7 +166,7 @@ public function clear() } // clear out the session - $this->data = array(); + $this->data = []; // reconnect the bags to the session $this->loadSession(); @@ -242,11 +238,11 @@ protected function generateId() protected function loadSession() { - $bags = array_merge($this->bags, array($this->metadataBag)); + $bags = array_merge($this->bags, [$this->metadataBag]); foreach ($bags as $bag) { $key = $bag->getStorageKey(); - $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array(); + $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : []; $bag->initialize($this->data[$key]); } diff --git a/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php index 14f4270..02fe4da 100644 --- a/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php +++ b/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php @@ -27,11 +27,10 @@ class MockFileSessionStorage extends MockArraySessionStorage private $savePath; /** - * @param string $savePath Path of directory to save session files - * @param string $name Session name - * @param MetadataBag $metaBag MetadataBag instance + * @param string $savePath Path of directory to save session files + * @param string $name Session name */ - public function __construct($savePath = null, $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(); @@ -98,7 +97,7 @@ public function save() unset($data[$key]); } } - if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) { + if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) { unset($data[$key]); } @@ -122,7 +121,7 @@ public function save() * Deletes a session from persistent storage. * Deliberately leaves session data in memory intact. */ - private function destroy() + private function destroy(): void { if (is_file($this->getFilePath())) { unlink($this->getFilePath()); @@ -131,10 +130,8 @@ private function destroy() /** * Calculate path to file. - * - * @return string File path */ - private function getFilePath() + private function getFilePath(): string { return $this->savePath.'/'.$this->id.'.mocksess'; } @@ -142,10 +139,10 @@ private function getFilePath() /** * Reads session from storage and loads session. */ - private function read() + private function read(): void { $filePath = $this->getFilePath(); - $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array(); + $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : []; $this->loadSession(); } diff --git a/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php index a18f812..4085bd6 100644 --- a/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php +++ b/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionUtils; use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; @@ -26,7 +27,7 @@ class NativeSessionStorage implements SessionStorageInterface /** * @var SessionBagInterface[] */ - protected $bags = array(); + protected $bags = []; /** * @var bool @@ -48,13 +49,18 @@ class NativeSessionStorage implements SessionStorageInterface */ protected $metadataBag; + /** + * @var string|null + */ + private $emulateSameSite; + /** * Depending on how you want the storage driver to behave you probably * want to override this constructor entirely. * * List of options for $options array with their defaults. * - * @see http://php.net/session.configuration for options + * @see https://php.net/session.configuration for options * but we omit 'session.' from the beginning of the keys for convenience. * * ("auto_start", is not supported as it tells PHP to start a session before @@ -67,13 +73,10 @@ class NativeSessionStorage implements SessionStorageInterface * cookie_lifetime, "0" * cookie_path, "/" * cookie_secure, "" - * entropy_file, "" - * entropy_length, "0" + * cookie_samesite, null * gc_divisor, "100" * gc_maxlifetime, "1440" * gc_probability, "1" - * hash_bits_per_character, "4" - * hash_function, "0" * lazy_write, "1" * name, "PHPSESSID" * referer_check, "" @@ -94,18 +97,21 @@ class NativeSessionStorage implements SessionStorageInterface * trans_sid_hosts, $_SERVER['HTTP_HOST'] * trans_sid_tags, "a=href,area=href,frame=src,form=" * - * @param array $options Session configuration options - * @param \SessionHandlerInterface|null $handler - * @param MetadataBag $metaBag MetadataBag + * @param AbstractProxy|\SessionHandlerInterface|null $handler */ - public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null) + public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null) { - $options += array( + if (!\extension_loaded('session')) { + throw new \LogicException('PHP extension "session" is required.'); + } + + $options += [ 'cache_limiter' => '', 'cache_expire' => 0, 'use_cookies' => 1, 'lazy_write' => 1, - ); + 'use_strict_mode' => 1, + ]; session_register_shutdown(); @@ -133,7 +139,7 @@ public function start() return true; } - if (\PHP_SESSION_ACTIVE === session_status()) { + if (PHP_SESSION_ACTIVE === session_status()) { throw new \RuntimeException('Failed to start the session: already started by PHP.'); } @@ -146,6 +152,13 @@ public function start() throw new \RuntimeException('Failed to start the session'); } + if (null !== $this->emulateSameSite) { + $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id()); + if (null !== $originalCookie) { + header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false); + } + } + $this->loadSession(); return true; @@ -189,7 +202,7 @@ public function setName($name) public function regenerate($destroy = false, $lifetime = null) { // Cannot regenerate the session ID for non-active sessions. - if (\PHP_SESSION_ACTIVE !== session_status()) { + if (PHP_SESSION_ACTIVE !== session_status()) { return false; } @@ -208,9 +221,16 @@ public function regenerate($destroy = false, $lifetime = null) $isRegenerated = session_regenerate_id($destroy); // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it. - // @see https://bugs.php.net/bug.php?id=70013 + // @see https://bugs.php.net/70013 $this->loadSession(); + if (null !== $this->emulateSameSite) { + $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id()); + if (null !== $originalCookie) { + header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false); + } + } + return $isRegenerated; } @@ -219,6 +239,7 @@ public function regenerate($destroy = false, $lifetime = null) */ public function save() { + // Store a copy so we can restore the bags in case the session was not left empty $session = $_SESSION; foreach ($this->bags as $bag) { @@ -226,7 +247,7 @@ public function save() unset($_SESSION[$key]); } } - if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) { + if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) { unset($_SESSION[$key]); } @@ -244,7 +265,11 @@ public function save() session_write_close(); } finally { restore_error_handler(); - $_SESSION = $session; + + // Restore only if not empty + if ($_SESSION) { + $_SESSION = $session; + } } $this->closed = true; @@ -262,7 +287,7 @@ public function clear() } // clear out the session - $_SESSION = array(); + $_SESSION = []; // reconnect the bags to the session $this->loadSession(); @@ -331,31 +356,36 @@ public function isStarted() * For convenience we omit 'session.' from the beginning of the keys. * Explicitly ignores other ini keys. * - * @param array $options Session ini directives array(key => value) + * @param array $options Session ini directives [key => value] * - * @see http://php.net/session.configuration + * @see https://php.net/session.configuration */ public function setOptions(array $options) { - if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) { + if (headers_sent() || PHP_SESSION_ACTIVE === session_status()) { return; } - $validOptions = array_flip(array( + $validOptions = array_flip([ 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly', - 'cookie_lifetime', 'cookie_path', 'cookie_secure', - 'entropy_file', 'entropy_length', 'gc_divisor', - 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', - 'hash_function', 'lazy_write', 'name', 'referer_check', + 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite', + 'gc_divisor', 'gc_maxlifetime', 'gc_probability', + 'lazy_write', 'name', 'referer_check', 'serialize_handler', 'use_strict_mode', 'use_cookies', 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags', 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags', - )); + ]); foreach ($options as $key => $value) { if (isset($validOptions[$key])) { + if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) { + // PHP < 7.3 does not support same_site cookies. We will emulate it in + // the start() method instead. + $this->emulateSameSite = $value; + continue; + } ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value); } } @@ -371,15 +401,13 @@ public function setOptions(array $options) * ini_set('session.save_path', '/tmp'); * * or pass in a \SessionHandler instance which configures session.save_handler in the - * constructor, for a template see NativeFileSessionHandler or use handlers in - * composer package drak/native-session + * constructor, for a template see NativeFileSessionHandler. * - * @see http://php.net/session-set-save-handler - * @see http://php.net/sessionhandlerinterface - * @see http://php.net/sessionhandler - * @see http://github.com/drak/NativeSession + * @see https://php.net/session-set-save-handler + * @see https://php.net/sessionhandlerinterface + * @see https://php.net/sessionhandler * - * @param \SessionHandlerInterface|null $saveHandler + * @param AbstractProxy|\SessionHandlerInterface|null $saveHandler * * @throws \InvalidArgumentException */ @@ -399,7 +427,7 @@ public function setSaveHandler($saveHandler = null) } $this->saveHandler = $saveHandler; - if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) { + if (headers_sent() || PHP_SESSION_ACTIVE === session_status()) { return; } @@ -422,11 +450,11 @@ protected function loadSession(array &$session = null) $session = &$_SESSION; } - $bags = array_merge($this->bags, array($this->metadataBag)); + $bags = array_merge($this->bags, [$this->metadataBag]); foreach ($bags as $bag) { $key = $bag->getStorageKey(); - $session[$key] = isset($session[$key]) ? $session[$key] : array(); + $session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : []; $bag->initialize($session[$key]); } diff --git a/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php index 662ed50..72dbef1 100644 --- a/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php +++ b/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php @@ -11,6 +11,8 @@ namespace Symfony\Component\HttpFoundation\Session\Storage; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; + /** * Allows session to be started by PHP and managed by Symfony. * @@ -19,11 +21,14 @@ class PhpBridgeSessionStorage extends NativeSessionStorage { /** - * @param \SessionHandlerInterface|null $handler - * @param MetadataBag $metaBag MetadataBag + * @param AbstractProxy|\SessionHandlerInterface|null $handler */ public function __construct($handler = null, MetadataBag $metaBag = null) { + if (!\extension_loaded('session')) { + throw new \LogicException('PHP extension "session" is required.'); + } + $this->setMetadataBag($metaBag); $this->setSaveHandler($handler); } diff --git a/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php index 09c9248..cd0635a 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php +++ b/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php @@ -31,7 +31,7 @@ abstract class AbstractProxy /** * Gets the session.save_handler name. * - * @return string + * @return string|null */ public function getSaveHandlerName() { @@ -65,7 +65,7 @@ public function isWrapper() */ public function isActive() { - return \PHP_SESSION_ACTIVE === session_status(); + return PHP_SESSION_ACTIVE === session_status(); } /** diff --git a/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php deleted file mode 100644 index 082eed1..0000000 --- a/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; - -@trigger_error('The '.__NAMESPACE__.'\NativeProxy class is deprecated since Symfony 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED); - -/** - * This proxy is built-in session handlers in PHP 5.3.x. - * - * @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly. - * - * @author Drak - */ -class NativeProxy extends AbstractProxy -{ - public function __construct() - { - // this makes an educated guess as to what the handler is since it should already be set. - $this->saveHandlerName = ini_get('session.save_handler'); - } - - /** - * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. - * - * @return bool False - */ - public function isWrapper() - { - return false; - } -} diff --git a/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php index b11cc39..de4f550 100644 --- a/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php +++ b/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php @@ -36,7 +36,7 @@ public function getHandler() // \SessionHandlerInterface /** - * {@inheritdoc} + * @return bool */ public function open($savePath, $sessionName) { @@ -44,7 +44,7 @@ public function open($savePath, $sessionName) } /** - * {@inheritdoc} + * @return bool */ public function close() { @@ -52,7 +52,7 @@ public function close() } /** - * {@inheritdoc} + * @return string */ public function read($sessionId) { @@ -60,7 +60,7 @@ public function read($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function write($sessionId, $data) { @@ -68,7 +68,7 @@ public function write($sessionId, $data) } /** - * {@inheritdoc} + * @return bool */ public function destroy($sessionId) { @@ -76,7 +76,7 @@ public function destroy($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function gc($maxlifetime) { @@ -84,7 +84,7 @@ public function gc($maxlifetime) } /** - * {@inheritdoc} + * @return bool */ public function validateId($sessionId) { @@ -92,7 +92,7 @@ public function validateId($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function updateTimestamp($sessionId, $data) { diff --git a/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php index 66e8b33..eeb396a 100644 --- a/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php +++ b/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php @@ -77,7 +77,7 @@ public function setName($name); * only delete the session data from persistent storage. * * Care: When regenerating the session ID no locking is involved in PHP's - * session design. See https://bugs.php.net/bug.php?id=61470 for a discussion. + * session design. See https://bugs.php.net/61470 for a discussion. * So you must make sure the regenerated session is saved BEFORE sending the * headers with the new ID. Symfony's HttpKernel offers a listener for this. * See Symfony\Component\HttpKernel\EventListener\SaveSessionListener. diff --git a/vendor/symfony/http-foundation/StreamedResponse.php b/vendor/symfony/http-foundation/StreamedResponse.php index 3a55d5d..ef8095b 100644 --- a/vendor/symfony/http-foundation/StreamedResponse.php +++ b/vendor/symfony/http-foundation/StreamedResponse.php @@ -35,7 +35,7 @@ class StreamedResponse extends Response * @param int $status The response status code * @param array $headers An array of response headers */ - public function __construct(callable $callback = null, $status = 200, $headers = array()) + public function __construct(callable $callback = null, int $status = 200, array $headers = []) { parent::__construct(null, $status, $headers); @@ -55,7 +55,7 @@ public function __construct(callable $callback = null, $status = 200, $headers = * * @return static */ - public static function create($callback = null, $status = 200, $headers = array()) + public static function create($callback = null, $status = 200, $headers = []) { return new static($callback, $status, $headers); } @@ -63,8 +63,6 @@ public static function create($callback = null, $status = 200, $headers = array( /** * Sets the PHP callback associated with this Response. * - * @param callable $callback A valid PHP callback - * * @return $this */ public function setCallback(callable $callback) @@ -111,7 +109,7 @@ public function sendContent() throw new \LogicException('The Response callback must not be null.'); } - \call_user_func($this->callback); + ($this->callback)(); return $this; } @@ -136,8 +134,6 @@ public function setContent($content) /** * {@inheritdoc} - * - * @return false */ public function getContent() { diff --git a/vendor/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php b/vendor/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php new file mode 100644 index 0000000..cb216ea --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Request; + +final class RequestAttributeValueSame extends Constraint +{ + private $name; + private $value; + + public function __construct(string $name, string $value) + { + $this->name = $name; + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('has attribute "%s" with value "%s"', $this->name, $this->value); + } + + /** + * @param Request $request + * + * {@inheritdoc} + */ + protected function matches($request): bool + { + return $this->value === $request->attributes->get($this->name); + } + + /** + * @param Request $request + * + * {@inheritdoc} + */ + protected function failureDescription($request): string + { + return 'the Request '.$this->toString(); + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php new file mode 100644 index 0000000..554e1a1 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseCookieValueSame extends Constraint +{ + private $name; + private $value; + private $path; + private $domain; + + public function __construct(string $name, string $value, string $path = '/', string $domain = null) + { + $this->name = $name; + $this->value = $value; + $this->path = $path; + $this->domain = $domain; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + $str = sprintf('has cookie "%s"', $this->name); + if ('/' !== $this->path) { + $str .= sprintf(' with path "%s"', $this->path); + } + if ($this->domain) { + $str .= sprintf(' for domain "%s"', $this->domain); + } + $str .= sprintf(' with value "%s"', $this->value); + + return $str; + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + $cookie = $this->getCookie($response); + if (!$cookie) { + return false; + } + + return $this->value === $cookie->getValue(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } + + protected function getCookie(Response $response): ?Cookie + { + $cookies = $response->headers->getCookies(); + + $filteredCookies = array_filter($cookies, function (Cookie $cookie) { + return $cookie->getName() === $this->name && $cookie->getPath() === $this->path && $cookie->getDomain() === $this->domain; + }); + + return reset($filteredCookies) ?: null; + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php new file mode 100644 index 0000000..eae9e27 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseHasCookie extends Constraint +{ + private $name; + private $path; + private $domain; + + public function __construct(string $name, string $path = '/', string $domain = null) + { + $this->name = $name; + $this->path = $path; + $this->domain = $domain; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + $str = sprintf('has cookie "%s"', $this->name); + if ('/' !== $this->path) { + $str .= sprintf(' with path "%s"', $this->path); + } + if ($this->domain) { + $str .= sprintf(' for domain "%s"', $this->domain); + } + + return $str; + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + return null !== $this->getCookie($response); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } + + private function getCookie(Response $response): ?Cookie + { + $cookies = $response->headers->getCookies(); + + $filteredCookies = array_filter($cookies, function (Cookie $cookie) { + return $cookie->getName() === $this->name && $cookie->getPath() === $this->path && $cookie->getDomain() === $this->domain; + }); + + return reset($filteredCookies) ?: null; + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php new file mode 100644 index 0000000..68ad827 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseHasHeader extends Constraint +{ + private $headerName; + + public function __construct(string $headerName) + { + $this->headerName = $headerName; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('has header "%s"', $this->headerName); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + return $response->headers->has($this->headerName); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php new file mode 100644 index 0000000..a27d0c7 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseHeaderSame extends Constraint +{ + private $headerName; + private $expectedValue; + + public function __construct(string $headerName, string $expectedValue) + { + $this->headerName = $headerName; + $this->expectedValue = $expectedValue; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('has header "%s" with value "%s"', $this->headerName, $this->expectedValue); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + return $this->expectedValue === $response->headers->get($this->headerName, null); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php new file mode 100644 index 0000000..8c4b883 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseIsRedirected extends Constraint +{ + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'is redirected'; + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + return $response->isRedirect(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function additionalFailureDescription($response): string + { + return (string) $response; + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php new file mode 100644 index 0000000..9c66558 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseIsSuccessful extends Constraint +{ + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'is successful'; + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + return $response->isSuccessful(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function additionalFailureDescription($response): string + { + return (string) $response; + } +} diff --git a/vendor/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php b/vendor/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php new file mode 100644 index 0000000..72bb000 --- /dev/null +++ b/vendor/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\HttpFoundation\Response; + +final class ResponseStatusCodeSame extends Constraint +{ + private $statusCode; + + public function __construct(int $statusCode) + { + $this->statusCode = $statusCode; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'status code is '.$this->statusCode; + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function matches($response): bool + { + return $this->statusCode === $response->getStatusCode(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function failureDescription($response): string + { + return 'the Response '.$this->toString(); + } + + /** + * @param Response $response + * + * {@inheritdoc} + */ + protected function additionalFailureDescription($response): string + { + return (string) $response; + } +} diff --git a/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php b/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php deleted file mode 100644 index cb43bb3..0000000 --- a/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\AcceptHeaderItem; - -class AcceptHeaderItemTest extends TestCase -{ - /** - * @dataProvider provideFromStringData - */ - public function testFromString($string, $value, array $attributes) - { - $item = AcceptHeaderItem::fromString($string); - $this->assertEquals($value, $item->getValue()); - $this->assertEquals($attributes, $item->getAttributes()); - } - - public function provideFromStringData() - { - return array( - array( - 'text/html', - 'text/html', array(), - ), - array( - '"this;should,not=matter"', - 'this;should,not=matter', array(), - ), - array( - "text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true", - 'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'), - ), - array( - '"this;should,not=matter";charset=utf-8', - 'this;should,not=matter', array('charset' => 'utf-8'), - ), - ); - } - - /** - * @dataProvider provideToStringData - */ - public function testToString($value, array $attributes, $string) - { - $item = new AcceptHeaderItem($value, $attributes); - $this->assertEquals($string, (string) $item); - } - - public function provideToStringData() - { - return array( - array( - 'text/html', array(), - 'text/html', - ), - array( - 'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'), - 'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true', - ), - ); - } - - public function testValue() - { - $item = new AcceptHeaderItem('value', array()); - $this->assertEquals('value', $item->getValue()); - - $item->setValue('new value'); - $this->assertEquals('new value', $item->getValue()); - - $item->setValue(1); - $this->assertEquals('1', $item->getValue()); - } - - public function testQuality() - { - $item = new AcceptHeaderItem('value', array()); - $this->assertEquals(1.0, $item->getQuality()); - - $item->setQuality(0.5); - $this->assertEquals(0.5, $item->getQuality()); - - $item->setAttribute('q', 0.75); - $this->assertEquals(0.75, $item->getQuality()); - $this->assertFalse($item->hasAttribute('q')); - } - - public function testAttribute() - { - $item = new AcceptHeaderItem('value', array()); - $this->assertEquals(array(), $item->getAttributes()); - $this->assertFalse($item->hasAttribute('test')); - $this->assertNull($item->getAttribute('test')); - $this->assertEquals('default', $item->getAttribute('test', 'default')); - - $item->setAttribute('test', 'value'); - $this->assertEquals(array('test' => 'value'), $item->getAttributes()); - $this->assertTrue($item->hasAttribute('test')); - $this->assertEquals('value', $item->getAttribute('test')); - $this->assertEquals('value', $item->getAttribute('test', 'default')); - } -} diff --git a/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php b/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php deleted file mode 100644 index 9929eac..0000000 --- a/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\AcceptHeader; -use Symfony\Component\HttpFoundation\AcceptHeaderItem; - -class AcceptHeaderTest extends TestCase -{ - public function testFirst() - { - $header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c'); - $this->assertSame('text/html', $header->first()->getValue()); - } - - /** - * @dataProvider provideFromStringData - */ - public function testFromString($string, array $items) - { - $header = AcceptHeader::fromString($string); - $parsed = array_values($header->all()); - // reset index since the fixtures don't have them set - foreach ($parsed as $item) { - $item->setIndex(0); - } - $this->assertEquals($items, $parsed); - } - - public function provideFromStringData() - { - return array( - array('', array()), - array('gzip', array(new AcceptHeaderItem('gzip'))), - array('gzip,deflate,sdch', array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))), - array("gzip, deflate\t,sdch", array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))), - array('"this;should,not=matter"', array(new AcceptHeaderItem('this;should,not=matter'))), - ); - } - - /** - * @dataProvider provideToStringData - */ - public function testToString(array $items, $string) - { - $header = new AcceptHeader($items); - $this->assertEquals($string, (string) $header); - } - - public function provideToStringData() - { - return array( - array(array(), ''), - array(array(new AcceptHeaderItem('gzip')), 'gzip'), - array(array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')), 'gzip,deflate,sdch'), - array(array(new AcceptHeaderItem('this;should,not=matter')), 'this;should,not=matter'), - ); - } - - /** - * @dataProvider provideFilterData - */ - public function testFilter($string, $filter, array $values) - { - $header = AcceptHeader::fromString($string)->filter($filter); - $this->assertEquals($values, array_keys($header->all())); - } - - public function provideFilterData() - { - return array( - array('fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', array('fr-FR', 'fr')), - ); - } - - /** - * @dataProvider provideSortingData - */ - public function testSorting($string, array $values) - { - $header = AcceptHeader::fromString($string); - $this->assertEquals($values, array_keys($header->all())); - } - - public function provideSortingData() - { - return array( - 'quality has priority' => array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')), - 'order matters when q is equal' => array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')), - 'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')), - ); - } -} diff --git a/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php b/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php deleted file mode 100644 index 157ab90..0000000 --- a/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php +++ /dev/null @@ -1,93 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\ApacheRequest; - -class ApacheRequestTest extends TestCase -{ - /** - * @dataProvider provideServerVars - */ - public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo) - { - $request = new ApacheRequest(); - $request->server->replace($server); - - $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct'); - $this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct'); - $this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct'); - } - - public function provideServerVars() - { - return array( - array( - array( - 'REQUEST_URI' => '/foo/app_dev.php/bar', - 'SCRIPT_NAME' => '/foo/app_dev.php', - 'PATH_INFO' => '/bar', - ), - '/foo/app_dev.php/bar', - '/foo/app_dev.php', - '/bar', - ), - array( - array( - 'REQUEST_URI' => '/foo/bar', - 'SCRIPT_NAME' => '/foo/app_dev.php', - ), - '/foo/bar', - '/foo', - '/bar', - ), - array( - array( - 'REQUEST_URI' => '/app_dev.php/foo/bar', - 'SCRIPT_NAME' => '/app_dev.php', - 'PATH_INFO' => '/foo/bar', - ), - '/app_dev.php/foo/bar', - '/app_dev.php', - '/foo/bar', - ), - array( - array( - 'REQUEST_URI' => '/foo/bar', - 'SCRIPT_NAME' => '/app_dev.php', - ), - '/foo/bar', - '', - '/foo/bar', - ), - array( - array( - 'REQUEST_URI' => '/app_dev.php', - 'SCRIPT_NAME' => '/app_dev.php', - ), - '/app_dev.php', - '/app_dev.php', - '/', - ), - array( - array( - 'REQUEST_URI' => '/', - 'SCRIPT_NAME' => '/app_dev.php', - ), - '/', - '', - '/', - ), - ); - } -} diff --git a/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php b/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php deleted file mode 100644 index d21791f..0000000 --- a/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php +++ /dev/null @@ -1,365 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\File\Stream; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\HttpFoundation\Tests\File\FakeFile; - -class BinaryFileResponseTest extends ResponseTestCase -{ - public function testConstruction() - { - $file = __DIR__.'/../README.md'; - $response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true); - $this->assertEquals(404, $response->getStatusCode()); - $this->assertEquals('Foo', $response->headers->get('X-Header')); - $this->assertTrue($response->headers->has('ETag')); - $this->assertTrue($response->headers->has('Last-Modified')); - $this->assertFalse($response->headers->has('Content-Disposition')); - - $response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE); - $this->assertEquals(404, $response->getStatusCode()); - $this->assertFalse($response->headers->has('ETag')); - $this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition')); - } - - public function testConstructWithNonAsciiFilename() - { - touch(sys_get_temp_dir().'/fööö.html'); - - $response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment'); - - @unlink(sys_get_temp_dir().'/fööö.html'); - - $this->assertSame('fööö.html', $response->getFile()->getFilename()); - } - - /** - * @expectedException \LogicException - */ - public function testSetContent() - { - $response = new BinaryFileResponse(__FILE__); - $response->setContent('foo'); - } - - public function testGetContent() - { - $response = new BinaryFileResponse(__FILE__); - $this->assertFalse($response->getContent()); - } - - public function testSetContentDispositionGeneratesSafeFallbackFilename() - { - $response = new BinaryFileResponse(__FILE__); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html'); - - $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition')); - } - - public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename() - { - $response = new BinaryFileResponse(__FILE__); - - $iso88591EncodedFilename = utf8_decode('föö.html'); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename); - - // the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one) - $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition')); - } - - /** - * @dataProvider provideRanges - */ - public function testRequests($requestRange, $offset, $length, $responseRange) - { - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); - - // do a request to get the ETag - $request = Request::create('/'); - $response->prepare($request); - $etag = $response->headers->get('ETag'); - - // prepare a request for a range of the testing file - $request = Request::create('/'); - $request->headers->set('If-Range', $etag); - $request->headers->set('Range', $requestRange); - - $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); - fseek($file, $offset); - $data = fread($file, $length); - fclose($file); - - $this->expectOutputString($data); - $response = clone $response; - $response->prepare($request); - $response->sendContent(); - - $this->assertEquals(206, $response->getStatusCode()); - $this->assertEquals($responseRange, $response->headers->get('Content-Range')); - $this->assertSame($length, $response->headers->get('Content-Length')); - } - - /** - * @dataProvider provideRanges - */ - public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange) - { - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); - - // do a request to get the LastModified - $request = Request::create('/'); - $response->prepare($request); - $lastModified = $response->headers->get('Last-Modified'); - - // prepare a request for a range of the testing file - $request = Request::create('/'); - $request->headers->set('If-Range', $lastModified); - $request->headers->set('Range', $requestRange); - - $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); - fseek($file, $offset); - $data = fread($file, $length); - fclose($file); - - $this->expectOutputString($data); - $response = clone $response; - $response->prepare($request); - $response->sendContent(); - - $this->assertEquals(206, $response->getStatusCode()); - $this->assertEquals($responseRange, $response->headers->get('Content-Range')); - } - - public function provideRanges() - { - return array( - array('bytes=1-4', 1, 4, 'bytes 1-4/35'), - array('bytes=-5', 30, 5, 'bytes 30-34/35'), - array('bytes=30-', 30, 5, 'bytes 30-34/35'), - array('bytes=30-30', 30, 1, 'bytes 30-30/35'), - array('bytes=30-34', 30, 5, 'bytes 30-34/35'), - ); - } - - public function testRangeRequestsWithoutLastModifiedDate() - { - // prevent auto last modified - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false); - - // prepare a request for a range of the testing file - $request = Request::create('/'); - $request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT'); - $request->headers->set('Range', 'bytes=1-4'); - - $this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif')); - $response = clone $response; - $response->prepare($request); - $response->sendContent(); - - $this->assertEquals(200, $response->getStatusCode()); - $this->assertNull($response->headers->get('Content-Range')); - } - - /** - * @dataProvider provideFullFileRanges - */ - public function testFullFileRequests($requestRange) - { - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); - - // prepare a request for a range of the testing file - $request = Request::create('/'); - $request->headers->set('Range', $requestRange); - - $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); - $data = fread($file, 35); - fclose($file); - - $this->expectOutputString($data); - $response = clone $response; - $response->prepare($request); - $response->sendContent(); - - $this->assertEquals(200, $response->getStatusCode()); - } - - public function provideFullFileRanges() - { - return array( - array('bytes=0-'), - array('bytes=0-34'), - array('bytes=-35'), - // Syntactical invalid range-request should also return the full resource - array('bytes=20-10'), - array('bytes=50-40'), - ); - } - - public function testUnpreparedResponseSendsFullFile() - { - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200); - - $data = file_get_contents(__DIR__.'/File/Fixtures/test.gif'); - - $this->expectOutputString($data); - $response = clone $response; - $response->sendContent(); - - $this->assertEquals(200, $response->getStatusCode()); - } - - /** - * @dataProvider provideInvalidRanges - */ - public function testInvalidRequests($requestRange) - { - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); - - // prepare a request for a range of the testing file - $request = Request::create('/'); - $request->headers->set('Range', $requestRange); - - $response = clone $response; - $response->prepare($request); - $response->sendContent(); - - $this->assertEquals(416, $response->getStatusCode()); - $this->assertEquals('bytes */35', $response->headers->get('Content-Range')); - } - - public function provideInvalidRanges() - { - return array( - array('bytes=-40'), - array('bytes=30-40'), - ); - } - - /** - * @dataProvider provideXSendfileFiles - */ - public function testXSendfile($file) - { - $request = Request::create('/'); - $request->headers->set('X-Sendfile-Type', 'X-Sendfile'); - - BinaryFileResponse::trustXSendfileTypeHeader(); - $response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream')); - $response->prepare($request); - - $this->expectOutputString(''); - $response->sendContent(); - - $this->assertContains('README.md', $response->headers->get('X-Sendfile')); - } - - public function provideXSendfileFiles() - { - return array( - array(__DIR__.'/../README.md'), - array('file://'.__DIR__.'/../README.md'), - ); - } - - /** - * @dataProvider getSampleXAccelMappings - */ - public function testXAccelMapping($realpath, $mapping, $virtual) - { - $request = Request::create('/'); - $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect'); - $request->headers->set('X-Accel-Mapping', $mapping); - - $file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test'); - - BinaryFileResponse::trustXSendfileTypeHeader(); - $response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream')); - $reflection = new \ReflectionObject($response); - $property = $reflection->getProperty('file'); - $property->setAccessible(true); - $property->setValue($response, $file); - - $response->prepare($request); - $this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect')); - } - - public function testDeleteFileAfterSend() - { - $request = Request::create('/'); - - $path = __DIR__.'/File/Fixtures/to_delete'; - touch($path); - $realPath = realpath($path); - $this->assertFileExists($realPath); - - $response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream')); - $response->deleteFileAfterSend(true); - - $response->prepare($request); - $response->sendContent(); - - $this->assertFileNotExists($path); - } - - public function testAcceptRangeOnUnsafeMethods() - { - $request = Request::create('/', 'POST'); - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); - $response->prepare($request); - - $this->assertEquals('none', $response->headers->get('Accept-Ranges')); - } - - public function testAcceptRangeNotOverriden() - { - $request = Request::create('/', 'POST'); - $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); - $response->headers->set('Accept-Ranges', 'foo'); - $response->prepare($request); - - $this->assertEquals('foo', $response->headers->get('Accept-Ranges')); - } - - public function getSampleXAccelMappings() - { - return array( - array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'), - array('/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'), - ); - } - - public function testStream() - { - $request = Request::create('/'); - $response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, array('Content-Type' => 'text/plain')); - $response->prepare($request); - - $this->assertNull($response->headers->get('Content-Length')); - } - - protected function provideResponse() - { - return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream')); - } - - public static function tearDownAfterClass() - { - $path = __DIR__.'/../Fixtures/to_delete'; - if (file_exists($path)) { - @unlink($path); - } - } -} diff --git a/vendor/symfony/http-foundation/Tests/CookieTest.php b/vendor/symfony/http-foundation/Tests/CookieTest.php deleted file mode 100644 index 14c45c9..0000000 --- a/vendor/symfony/http-foundation/Tests/CookieTest.php +++ /dev/null @@ -1,235 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Cookie; - -/** - * CookieTest. - * - * @author John Kary - * @author Hugo Hamon - * - * @group time-sensitive - */ -class CookieTest extends TestCase -{ - public function invalidNames() - { - return array( - array(''), - array(',MyName'), - array(';MyName'), - array(' MyName'), - array("\tMyName"), - array("\rMyName"), - array("\nMyName"), - array("\013MyName"), - array("\014MyName"), - ); - } - - /** - * @dataProvider invalidNames - * @expectedException \InvalidArgumentException - */ - public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name) - { - new Cookie($name); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testInvalidExpiration() - { - new Cookie('MyCookie', 'foo', 'bar'); - } - - public function testNegativeExpirationIsNotPossible() - { - $cookie = new Cookie('foo', 'bar', -100); - - $this->assertSame(0, $cookie->getExpiresTime()); - } - - public function testGetValue() - { - $value = 'MyValue'; - $cookie = new Cookie('MyCookie', $value); - - $this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value'); - } - - public function testGetPath() - { - $cookie = new Cookie('foo', 'bar'); - - $this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path'); - } - - public function testGetExpiresTime() - { - $cookie = new Cookie('foo', 'bar'); - - $this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date'); - - $cookie = new Cookie('foo', 'bar', $expire = time() + 3600); - - $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); - } - - public function testGetExpiresTimeIsCastToInt() - { - $cookie = new Cookie('foo', 'bar', 3600.9); - - $this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer'); - } - - public function testConstructorWithDateTime() - { - $expire = new \DateTime(); - $cookie = new Cookie('foo', 'bar', $expire); - - $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); - } - - /** - * @requires PHP 5.5 - */ - public function testConstructorWithDateTimeImmutable() - { - $expire = new \DateTimeImmutable(); - $cookie = new Cookie('foo', 'bar', $expire); - - $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); - } - - public function testGetExpiresTimeWithStringValue() - { - $value = '+1 day'; - $cookie = new Cookie('foo', 'bar', $value); - $expire = strtotime($value); - - $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1); - } - - public function testGetDomain() - { - $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com'); - - $this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid'); - } - - public function testIsSecure() - { - $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true); - - $this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS'); - } - - public function testIsHttpOnly() - { - $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true); - - $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP'); - } - - public function testCookieIsNotCleared() - { - $cookie = new Cookie('foo', 'bar', time() + 3600 * 24); - - $this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet'); - } - - public function testCookieIsCleared() - { - $cookie = new Cookie('foo', 'bar', time() - 20); - - $this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired'); - - $cookie = new Cookie('foo', 'bar'); - - $this->assertFalse($cookie->isCleared()); - - $cookie = new Cookie('foo', 'bar', 0); - - $this->assertFalse($cookie->isCleared()); - - $cookie = new Cookie('foo', 'bar', -1); - - $this->assertFalse($cookie->isCleared()); - } - - public function testToString() - { - $cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); - $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie'); - - $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); - $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)'); - - $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com'); - $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL'); - - $cookie = new Cookie('foo', 'bar', 0, '/', ''); - $this->assertEquals('foo=bar; path=/; httponly', (string) $cookie); - } - - public function testRawCookie() - { - $cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false); - $this->assertFalse($cookie->isRaw()); - $this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie); - - $cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true); - $this->assertTrue($cookie->isRaw()); - $this->assertEquals('foo=b+a+r; path=/', (string) $cookie); - } - - public function testGetMaxAge() - { - $cookie = new Cookie('foo', 'bar'); - $this->assertEquals(0, $cookie->getMaxAge()); - - $cookie = new Cookie('foo', 'bar', $expire = time() + 100); - $this->assertEquals($expire - time(), $cookie->getMaxAge()); - - $cookie = new Cookie('foo', 'bar', $expire = time() - 100); - $this->assertEquals(0, $cookie->getMaxAge()); - } - - public function testFromString() - { - $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly'); - $this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie); - - $cookie = Cookie::fromString('foo=bar', true); - $this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie); - } - - public function testFromStringWithHttpOnly() - { - $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly'); - $this->assertTrue($cookie->isHttpOnly()); - - $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure'); - $this->assertFalse($cookie->isHttpOnly()); - } - - public function testSameSiteAttributeIsCaseInsensitive() - { - $cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax'); - $this->assertEquals('lax', $cookie->getSameSite()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php b/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php deleted file mode 100644 index 1152e46..0000000 --- a/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use Symfony\Component\HttpFoundation\ExpressionRequestMatcher; -use Symfony\Component\HttpFoundation\Request; - -class ExpressionRequestMatcherTest extends TestCase -{ - /** - * @expectedException \LogicException - */ - public function testWhenNoExpressionIsSet() - { - $expressionRequestMatcher = new ExpressionRequestMatcher(); - $expressionRequestMatcher->matches(new Request()); - } - - /** - * @dataProvider provideExpressions - */ - public function testMatchesWhenParentMatchesIsTrue($expression, $expected) - { - $request = Request::create('/foo'); - $expressionRequestMatcher = new ExpressionRequestMatcher(); - - $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression); - $this->assertSame($expected, $expressionRequestMatcher->matches($request)); - } - - /** - * @dataProvider provideExpressions - */ - public function testMatchesWhenParentMatchesIsFalse($expression) - { - $request = Request::create('/foo'); - $request->attributes->set('foo', 'foo'); - $expressionRequestMatcher = new ExpressionRequestMatcher(); - $expressionRequestMatcher->matchAttribute('foo', 'bar'); - - $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression); - $this->assertFalse($expressionRequestMatcher->matches($request)); - } - - public function provideExpressions() - { - return array( - array('request.getMethod() == method', true), - array('request.getPathInfo() == path', true), - array('request.getHost() == host', true), - array('request.getClientIp() == ip', true), - array('request.attributes.all() == attributes', true), - array('request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true), - array('request.getMethod() != method', false), - array('request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false), - ); - } -} diff --git a/vendor/symfony/http-foundation/Tests/File/FakeFile.php b/vendor/symfony/http-foundation/Tests/File/FakeFile.php deleted file mode 100644 index c415989..0000000 --- a/vendor/symfony/http-foundation/Tests/File/FakeFile.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\File; - -use Symfony\Component\HttpFoundation\File\File as OrigFile; - -class FakeFile extends OrigFile -{ - private $realpath; - - public function __construct($realpath, $path) - { - $this->realpath = $realpath; - parent::__construct($path, false); - } - - public function isReadable() - { - return true; - } - - public function getRealpath() - { - return $this->realpath; - } - - public function getSize() - { - return 42; - } - - public function getMTime() - { - return time(); - } -} diff --git a/vendor/symfony/http-foundation/Tests/File/FileTest.php b/vendor/symfony/http-foundation/Tests/File/FileTest.php deleted file mode 100644 index dbd9c44..0000000 --- a/vendor/symfony/http-foundation/Tests/File/FileTest.php +++ /dev/null @@ -1,180 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\File; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\File\File; -use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; - -class FileTest extends TestCase -{ - protected $file; - - public function testGetMimeTypeUsesMimeTypeGuessers() - { - $file = new File(__DIR__.'/Fixtures/test.gif'); - $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif'); - - MimeTypeGuesser::getInstance()->register($guesser); - - $this->assertEquals('image/gif', $file->getMimeType()); - } - - public function testGuessExtensionWithoutGuesser() - { - $file = new File(__DIR__.'/Fixtures/directory/.empty'); - - $this->assertNull($file->guessExtension()); - } - - public function testGuessExtensionIsBasedOnMimeType() - { - $file = new File(__DIR__.'/Fixtures/test'); - $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif'); - - MimeTypeGuesser::getInstance()->register($guesser); - - $this->assertEquals('gif', $file->guessExtension()); - } - - /** - * @requires extension fileinfo - */ - public function testGuessExtensionWithReset() - { - $file = new File(__DIR__.'/Fixtures/other-file.example'); - $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif'); - MimeTypeGuesser::getInstance()->register($guesser); - - $this->assertEquals('gif', $file->guessExtension()); - - MimeTypeGuesser::reset(); - - $this->assertNull($file->guessExtension()); - } - - public function testConstructWhenFileNotExists() - { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); - - new File(__DIR__.'/Fixtures/not_here'); - } - - public function testMove() - { - $path = __DIR__.'/Fixtures/test.copy.gif'; - $targetDir = __DIR__.'/Fixtures/directory'; - $targetPath = $targetDir.'/test.copy.gif'; - @unlink($path); - @unlink($targetPath); - copy(__DIR__.'/Fixtures/test.gif', $path); - - $file = new File($path); - $movedFile = $file->move($targetDir); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile); - - $this->assertFileExists($targetPath); - $this->assertFileNotExists($path); - $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); - - @unlink($targetPath); - } - - public function testMoveWithNewName() - { - $path = __DIR__.'/Fixtures/test.copy.gif'; - $targetDir = __DIR__.'/Fixtures/directory'; - $targetPath = $targetDir.'/test.newname.gif'; - @unlink($path); - @unlink($targetPath); - copy(__DIR__.'/Fixtures/test.gif', $path); - - $file = new File($path); - $movedFile = $file->move($targetDir, 'test.newname.gif'); - - $this->assertFileExists($targetPath); - $this->assertFileNotExists($path); - $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); - - @unlink($targetPath); - } - - public function getFilenameFixtures() - { - return array( - array('original.gif', 'original.gif'), - array('..\\..\\original.gif', 'original.gif'), - array('../../original.gif', 'original.gif'), - array('файлfile.gif', 'файлfile.gif'), - array('..\\..\\файлfile.gif', 'файлfile.gif'), - array('../../файлfile.gif', 'файлfile.gif'), - ); - } - - /** - * @dataProvider getFilenameFixtures - */ - public function testMoveWithNonLatinName($filename, $sanitizedFilename) - { - $path = __DIR__.'/Fixtures/'.$sanitizedFilename; - $targetDir = __DIR__.'/Fixtures/directory/'; - $targetPath = $targetDir.$sanitizedFilename; - @unlink($path); - @unlink($targetPath); - copy(__DIR__.'/Fixtures/test.gif', $path); - - $file = new File($path); - $movedFile = $file->move($targetDir, $filename); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile); - - $this->assertFileExists($targetPath); - $this->assertFileNotExists($path); - $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); - - @unlink($targetPath); - } - - public function testMoveToAnUnexistentDirectory() - { - $sourcePath = __DIR__.'/Fixtures/test.copy.gif'; - $targetDir = __DIR__.'/Fixtures/directory/sub'; - $targetPath = $targetDir.'/test.copy.gif'; - @unlink($sourcePath); - @unlink($targetPath); - @rmdir($targetDir); - copy(__DIR__.'/Fixtures/test.gif', $sourcePath); - - $file = new File($sourcePath); - $movedFile = $file->move($targetDir); - - $this->assertFileExists($targetPath); - $this->assertFileNotExists($sourcePath); - $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); - - @unlink($sourcePath); - @unlink($targetPath); - @rmdir($targetDir); - } - - protected function createMockGuesser($path, $mimeType) - { - $guesser = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface')->getMock(); - $guesser - ->expects($this->once()) - ->method('guess') - ->with($this->equalTo($path)) - ->will($this->returnValue($mimeType)) - ; - - return $guesser; - } -} diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension b/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension deleted file mode 100644 index 4d1ae35..0000000 --- a/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension +++ /dev/null @@ -1 +0,0 @@ -f \ No newline at end of file diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty b/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty deleted file mode 100644 index e69de29..0000000 diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example b/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example deleted file mode 100644 index e69de29..0000000 diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/test b/vendor/symfony/http-foundation/Tests/File/Fixtures/test deleted file mode 100644 index b636f4b8df536b0a85e7cea1a6cf3f0bd3179b96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35 jcmZ?wbh9u|WMp7uXkcLY4+c66KmZb9U}AD%WUvMRyAlZ1 diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif b/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif deleted file mode 100644 index b636f4b8df536b0a85e7cea1a6cf3f0bd3179b96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35 jcmZ?wbh9u|WMp7uXkcLY4+c66KmZb9U}AD%WUvMRyAlZ1 diff --git a/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php b/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php deleted file mode 100644 index bb88807..0000000 --- a/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\File\MimeType; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser; -use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; - -/** - * @requires extension fileinfo - */ -class MimeTypeTest extends TestCase -{ - protected $path; - - public function testGuessImageWithoutExtension() - { - $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); - } - - public function testGuessImageWithDirectory() - { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); - - MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory'); - } - - public function testGuessImageWithFileBinaryMimeTypeGuesser() - { - $guesser = MimeTypeGuesser::getInstance(); - $guesser->register(new FileBinaryMimeTypeGuesser()); - $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); - } - - public function testGuessImageWithKnownExtension() - { - $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif')); - } - - public function testGuessFileWithUnknownExtension() - { - $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension')); - } - - public function testGuessWithIncorrectPath() - { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); - MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here'); - } - - public function testGuessWithNonReadablePath() - { - if ('\\' === \DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Can not verify chmod operations on Windows'); - } - - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - - $path = __DIR__.'/../Fixtures/to_delete'; - touch($path); - @chmod($path, 0333); - - if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException'); - MimeTypeGuesser::getInstance()->guess($path); - } else { - $this->markTestSkipped('Can not verify chmod operations, change of file permissions failed'); - } - } - - public static function tearDownAfterClass() - { - $path = __DIR__.'/../Fixtures/to_delete'; - if (file_exists($path)) { - @chmod($path, 0666); - @unlink($path); - } - } -} diff --git a/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php b/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php deleted file mode 100644 index 1a88d48..0000000 --- a/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php +++ /dev/null @@ -1,273 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\File; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\File\UploadedFile; - -class UploadedFileTest extends TestCase -{ - protected function setUp() - { - if (!ini_get('file_uploads')) { - $this->markTestSkipped('file_uploads is disabled in php.ini'); - } - } - - public function testConstructWhenFileNotExists() - { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); - - new UploadedFile( - __DIR__.'/Fixtures/not_here', - 'original.gif', - null - ); - } - - public function testFileUploadsWithNoMimeType() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - null, - filesize(__DIR__.'/Fixtures/test.gif'), - UPLOAD_ERR_OK - ); - - $this->assertEquals('application/octet-stream', $file->getClientMimeType()); - - if (\extension_loaded('fileinfo')) { - $this->assertEquals('image/gif', $file->getMimeType()); - } - } - - public function testFileUploadsWithUnknownMimeType() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/.unknownextension', - 'original.gif', - null, - filesize(__DIR__.'/Fixtures/.unknownextension'), - UPLOAD_ERR_OK - ); - - $this->assertEquals('application/octet-stream', $file->getClientMimeType()); - } - - public function testGuessClientExtension() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals('gif', $file->guessClientExtension()); - } - - public function testGuessClientExtensionWithIncorrectMimeType() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/jpeg', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals('jpeg', $file->guessClientExtension()); - } - - public function testErrorIsOkByDefault() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals(UPLOAD_ERR_OK, $file->getError()); - } - - public function testGetClientOriginalName() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals('original.gif', $file->getClientOriginalName()); - } - - public function testGetClientOriginalExtension() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals('gif', $file->getClientOriginalExtension()); - } - - /** - * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException - */ - public function testMoveLocalFileIsNotAllowed() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - UPLOAD_ERR_OK - ); - - $movedFile = $file->move(__DIR__.'/Fixtures/directory'); - } - - public function testMoveLocalFileIsAllowedInTestMode() - { - $path = __DIR__.'/Fixtures/test.copy.gif'; - $targetDir = __DIR__.'/Fixtures/directory'; - $targetPath = $targetDir.'/test.copy.gif'; - @unlink($path); - @unlink($targetPath); - copy(__DIR__.'/Fixtures/test.gif', $path); - - $file = new UploadedFile( - $path, - 'original.gif', - 'image/gif', - filesize($path), - UPLOAD_ERR_OK, - true - ); - - $movedFile = $file->move(__DIR__.'/Fixtures/directory'); - - $this->assertFileExists($targetPath); - $this->assertFileNotExists($path); - $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); - - @unlink($targetPath); - } - - public function testGetClientOriginalNameSanitizeFilename() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - '../../original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals('original.gif', $file->getClientOriginalName()); - } - - public function testGetSize() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - 'image/gif', - filesize(__DIR__.'/Fixtures/test.gif'), - null - ); - - $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); - - $file = new UploadedFile( - __DIR__.'/Fixtures/test', - 'original.gif', - 'image/gif' - ); - - $this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize()); - } - - public function testGetExtension() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - null - ); - - $this->assertEquals('gif', $file->getExtension()); - } - - public function testIsValid() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - null, - filesize(__DIR__.'/Fixtures/test.gif'), - UPLOAD_ERR_OK, - true - ); - - $this->assertTrue($file->isValid()); - } - - /** - * @dataProvider uploadedFileErrorProvider - */ - public function testIsInvalidOnUploadError($error) - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - null, - filesize(__DIR__.'/Fixtures/test.gif'), - $error - ); - - $this->assertFalse($file->isValid()); - } - - public function uploadedFileErrorProvider() - { - return array( - array(UPLOAD_ERR_INI_SIZE), - array(UPLOAD_ERR_FORM_SIZE), - array(UPLOAD_ERR_PARTIAL), - array(UPLOAD_ERR_NO_TMP_DIR), - array(UPLOAD_ERR_EXTENSION), - ); - } - - public function testIsInvalidIfNotHttpUpload() - { - $file = new UploadedFile( - __DIR__.'/Fixtures/test.gif', - 'original.gif', - null, - filesize(__DIR__.'/Fixtures/test.gif'), - UPLOAD_ERR_OK - ); - - $this->assertFalse($file->isValid()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/FileBagTest.php b/vendor/symfony/http-foundation/Tests/FileBagTest.php deleted file mode 100644 index b1bbba0..0000000 --- a/vendor/symfony/http-foundation/Tests/FileBagTest.php +++ /dev/null @@ -1,175 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\File\UploadedFile; -use Symfony\Component\HttpFoundation\FileBag; - -/** - * FileBagTest. - * - * @author Fabien Potencier - * @author Bulat Shakirzyanov - */ -class FileBagTest extends TestCase -{ - /** - * @expectedException \InvalidArgumentException - */ - public function testFileMustBeAnArrayOrUploadedFile() - { - new FileBag(array('file' => 'foo')); - } - - public function testShouldConvertsUploadedFiles() - { - $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); - - $bag = new FileBag(array('file' => array( - 'name' => basename($tmpFile), - 'type' => 'text/plain', - 'tmp_name' => $tmpFile, - 'error' => 0, - 'size' => 100, - ))); - - $this->assertEquals($file, $bag->get('file')); - } - - public function testShouldSetEmptyUploadedFilesToNull() - { - $bag = new FileBag(array('file' => array( - 'name' => '', - 'type' => '', - 'tmp_name' => '', - 'error' => UPLOAD_ERR_NO_FILE, - 'size' => 0, - ))); - - $this->assertNull($bag->get('file')); - } - - public function testShouldRemoveEmptyUploadedFilesForMultiUpload() - { - $bag = new FileBag(array('files' => array( - 'name' => array(''), - 'type' => array(''), - 'tmp_name' => array(''), - 'error' => array(UPLOAD_ERR_NO_FILE), - 'size' => array(0), - ))); - - $this->assertSame(array(), $bag->get('files')); - } - - public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray() - { - $bag = new FileBag(array('files' => array( - 'name' => array('file1' => ''), - 'type' => array('file1' => ''), - 'tmp_name' => array('file1' => ''), - 'error' => array('file1' => UPLOAD_ERR_NO_FILE), - 'size' => array('file1' => 0), - ))); - - $this->assertSame(array('file1' => null), $bag->get('files')); - } - - public function testShouldConvertUploadedFilesWithPhpBug() - { - $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); - - $bag = new FileBag(array( - 'child' => array( - 'name' => array( - 'file' => basename($tmpFile), - ), - 'type' => array( - 'file' => 'text/plain', - ), - 'tmp_name' => array( - 'file' => $tmpFile, - ), - 'error' => array( - 'file' => 0, - ), - 'size' => array( - 'file' => 100, - ), - ), - )); - - $files = $bag->all(); - $this->assertEquals($file, $files['child']['file']); - } - - public function testShouldConvertNestedUploadedFilesWithPhpBug() - { - $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); - - $bag = new FileBag(array( - 'child' => array( - 'name' => array( - 'sub' => array('file' => basename($tmpFile)), - ), - 'type' => array( - 'sub' => array('file' => 'text/plain'), - ), - 'tmp_name' => array( - 'sub' => array('file' => $tmpFile), - ), - 'error' => array( - 'sub' => array('file' => 0), - ), - 'size' => array( - 'sub' => array('file' => 100), - ), - ), - )); - - $files = $bag->all(); - $this->assertEquals($file, $files['child']['sub']['file']); - } - - public function testShouldNotConvertNestedUploadedFiles() - { - $tmpFile = $this->createTempFile(); - $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); - $bag = new FileBag(array('image' => array('file' => $file))); - - $files = $bag->all(); - $this->assertEquals($file, $files['image']['file']); - } - - protected function createTempFile() - { - return tempnam(sys_get_temp_dir().'/form_test', 'FormTest'); - } - - protected function setUp() - { - mkdir(sys_get_temp_dir().'/form_test', 0777, true); - } - - protected function tearDown() - { - foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) { - unlink($file); - } - - rmdir(sys_get_temp_dir().'/form_test'); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc deleted file mode 100644 index 0bdf9e4..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc +++ /dev/null @@ -1,43 +0,0 @@ -headers->set('Date', 'Sat, 12 Nov 1955 20:04:00 GMT'); - -return $r; diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected deleted file mode 100644 index bdb9d02..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected +++ /dev/null @@ -1,11 +0,0 @@ - -Warning: Expiry date cannot have a year greater than 9999 in %scookie_max_age.php on line 10 - -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: no-cache, private - [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT - [3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/ -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php deleted file mode 100644 index 8775a5c..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php +++ /dev/null @@ -1,10 +0,0 @@ -headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false)); -$r->sendHeaders(); - -setcookie('foo2', 'bar', 253402310800, '/'); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected deleted file mode 100644 index 0c09797..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected +++ /dev/null @@ -1,10 +0,0 @@ - -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: no-cache, private - [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT - [3] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/ - [4] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/ -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php deleted file mode 100644 index 2ca5b59..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php +++ /dev/null @@ -1,12 +0,0 @@ -headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true)); -$r->sendHeaders(); - -setrawcookie($str, $str, 0, '/', null, false, false); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected deleted file mode 100644 index cbde2cb..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected +++ /dev/null @@ -1,9 +0,0 @@ - -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: no-cache, private - [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT - [3] => Set-Cookie: CookieSamesiteLaxTest=LaxValue; path=/; httponly; samesite=lax -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php deleted file mode 100644 index 9a476f1..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php +++ /dev/null @@ -1,8 +0,0 @@ -headers->setCookie(new Cookie('CookieSamesiteLaxTest', 'LaxValue', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX)); -$r->sendHeaders(); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected deleted file mode 100644 index adc491f..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected +++ /dev/null @@ -1,9 +0,0 @@ - -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: no-cache, private - [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT - [3] => Set-Cookie: CookieSamesiteStrictTest=StrictValue; path=/; httponly; samesite=strict -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php deleted file mode 100644 index 3bcb41f..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php +++ /dev/null @@ -1,8 +0,0 @@ -headers->setCookie(new Cookie('CookieSamesiteStrictTest', 'StrictValue', 0, '/', null, false, true, false, Cookie::SAMESITE_STRICT)); -$r->sendHeaders(); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected deleted file mode 100644 index 14e44a3..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected +++ /dev/null @@ -1,10 +0,0 @@ - -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: no-cache, private - [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT - [3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ - [4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php deleted file mode 100644 index 05b9af3..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php +++ /dev/null @@ -1,12 +0,0 @@ -headers->setCookie(new Cookie($str, $str, 0, '', null, false, false)); -$r->sendHeaders(); - -setcookie($str, $str, 0, '/'); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected deleted file mode 100644 index 2b560f0..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected +++ /dev/null @@ -1,6 +0,0 @@ -The cookie name "Hello + world" contains invalid characters. -Array -( - [0] => Content-Type: text/plain; charset=utf-8 -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php deleted file mode 100644 index 3fe1571..0000000 --- a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php +++ /dev/null @@ -1,11 +0,0 @@ -headers->setCookie(new Cookie('Hello + world', 'hodor')); -} catch (\InvalidArgumentException $e) { - echo $e->getMessage(); -} diff --git a/vendor/symfony/http-foundation/Tests/HeaderBagTest.php b/vendor/symfony/http-foundation/Tests/HeaderBagTest.php deleted file mode 100644 index c5a437f..0000000 --- a/vendor/symfony/http-foundation/Tests/HeaderBagTest.php +++ /dev/null @@ -1,205 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\HeaderBag; - -class HeaderBagTest extends TestCase -{ - public function testConstructor() - { - $bag = new HeaderBag(array('foo' => 'bar')); - $this->assertTrue($bag->has('foo')); - } - - public function testToStringNull() - { - $bag = new HeaderBag(); - $this->assertEquals('', $bag->__toString()); - } - - public function testToStringNotNull() - { - $bag = new HeaderBag(array('foo' => 'bar')); - $this->assertEquals("Foo: bar\r\n", $bag->__toString()); - } - - public function testKeys() - { - $bag = new HeaderBag(array('foo' => 'bar')); - $keys = $bag->keys(); - $this->assertEquals('foo', $keys[0]); - } - - public function testGetDate() - { - $bag = new HeaderBag(array('foo' => 'Tue, 4 Sep 2012 20:00:00 +0200')); - $headerDate = $bag->getDate('foo'); - $this->assertInstanceOf('DateTime', $headerDate); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetDateException() - { - $bag = new HeaderBag(array('foo' => 'Tue')); - $headerDate = $bag->getDate('foo'); - } - - public function testGetCacheControlHeader() - { - $bag = new HeaderBag(); - $bag->addCacheControlDirective('public', '#a'); - $this->assertTrue($bag->hasCacheControlDirective('public')); - $this->assertEquals('#a', $bag->getCacheControlDirective('public')); - } - - public function testAll() - { - $bag = new HeaderBag(array('foo' => 'bar')); - $this->assertEquals(array('foo' => array('bar')), $bag->all(), '->all() gets all the input'); - - $bag = new HeaderBag(array('FOO' => 'BAR')); - $this->assertEquals(array('foo' => array('BAR')), $bag->all(), '->all() gets all the input key are lower case'); - } - - public function testReplace() - { - $bag = new HeaderBag(array('foo' => 'bar')); - - $bag->replace(array('NOPE' => 'BAR')); - $this->assertEquals(array('nope' => array('BAR')), $bag->all(), '->replace() replaces the input with the argument'); - $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input'); - } - - public function testGet() - { - $bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz')); - $this->assertEquals('bar', $bag->get('foo'), '->get return current value'); - $this->assertEquals('bar', $bag->get('FoO'), '->get key in case insensitive'); - $this->assertEquals(array('bar'), $bag->get('foo', 'nope', false), '->get return the value as array'); - - // defaults - $this->assertNull($bag->get('none'), '->get unknown values returns null'); - $this->assertEquals('default', $bag->get('none', 'default'), '->get unknown values returns default'); - $this->assertEquals(array('default'), $bag->get('none', 'default', false), '->get unknown values returns default as array'); - - $bag->set('foo', 'bor', false); - $this->assertEquals('bar', $bag->get('foo'), '->get return first value'); - $this->assertEquals(array('bar', 'bor'), $bag->get('foo', 'nope', false), '->get return all values as array'); - } - - public function testSetAssociativeArray() - { - $bag = new HeaderBag(); - $bag->set('foo', array('bad-assoc-index' => 'value')); - $this->assertSame('value', $bag->get('foo')); - $this->assertEquals(array('value'), $bag->get('foo', 'nope', false), 'assoc indices of multi-valued headers are ignored'); - } - - public function testContains() - { - $bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz')); - $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value'); - $this->assertTrue($bag->contains('fuzz', 'bizz'), '->contains second value'); - $this->assertFalse($bag->contains('nope', 'nope'), '->contains unknown value'); - $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value'); - - // Multiple values - $bag->set('foo', 'bor', false); - $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value'); - $this->assertTrue($bag->contains('foo', 'bor'), '->contains second value'); - $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value'); - } - - public function testCacheControlDirectiveAccessors() - { - $bag = new HeaderBag(); - $bag->addCacheControlDirective('public'); - - $this->assertTrue($bag->hasCacheControlDirective('public')); - $this->assertTrue($bag->getCacheControlDirective('public')); - $this->assertEquals('public', $bag->get('cache-control')); - - $bag->addCacheControlDirective('max-age', 10); - $this->assertTrue($bag->hasCacheControlDirective('max-age')); - $this->assertEquals(10, $bag->getCacheControlDirective('max-age')); - $this->assertEquals('max-age=10, public', $bag->get('cache-control')); - - $bag->removeCacheControlDirective('max-age'); - $this->assertFalse($bag->hasCacheControlDirective('max-age')); - } - - public function testCacheControlDirectiveParsing() - { - $bag = new HeaderBag(array('cache-control' => 'public, max-age=10')); - $this->assertTrue($bag->hasCacheControlDirective('public')); - $this->assertTrue($bag->getCacheControlDirective('public')); - - $this->assertTrue($bag->hasCacheControlDirective('max-age')); - $this->assertEquals(10, $bag->getCacheControlDirective('max-age')); - - $bag->addCacheControlDirective('s-maxage', 100); - $this->assertEquals('max-age=10, public, s-maxage=100', $bag->get('cache-control')); - } - - public function testCacheControlDirectiveParsingQuotedZero() - { - $bag = new HeaderBag(array('cache-control' => 'max-age="0"')); - $this->assertTrue($bag->hasCacheControlDirective('max-age')); - $this->assertEquals(0, $bag->getCacheControlDirective('max-age')); - } - - public function testCacheControlDirectiveOverrideWithReplace() - { - $bag = new HeaderBag(array('cache-control' => 'private, max-age=100')); - $bag->replace(array('cache-control' => 'public, max-age=10')); - $this->assertTrue($bag->hasCacheControlDirective('public')); - $this->assertTrue($bag->getCacheControlDirective('public')); - - $this->assertTrue($bag->hasCacheControlDirective('max-age')); - $this->assertEquals(10, $bag->getCacheControlDirective('max-age')); - } - - public function testCacheControlClone() - { - $headers = array('foo' => 'bar'); - $bag1 = new HeaderBag($headers); - $bag2 = new HeaderBag($bag1->all()); - - $this->assertEquals($bag1->all(), $bag2->all()); - } - - public function testGetIterator() - { - $headers = array('foo' => 'bar', 'hello' => 'world', 'third' => 'charm'); - $headerBag = new HeaderBag($headers); - - $i = 0; - foreach ($headerBag as $key => $val) { - ++$i; - $this->assertEquals(array($headers[$key]), $val); - } - - $this->assertEquals(\count($headers), $i); - } - - public function testCount() - { - $headers = array('foo' => 'bar', 'HELLO' => 'WORLD'); - $headerBag = new HeaderBag($headers); - - $this->assertCount(\count($headers), $headerBag); - } -} diff --git a/vendor/symfony/http-foundation/Tests/IpUtilsTest.php b/vendor/symfony/http-foundation/Tests/IpUtilsTest.php deleted file mode 100644 index 232a204..0000000 --- a/vendor/symfony/http-foundation/Tests/IpUtilsTest.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\IpUtils; - -class IpUtilsTest extends TestCase -{ - /** - * @dataProvider getIpv4Data - */ - public function testIpv4($matches, $remoteAddr, $cidr) - { - $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr)); - } - - public function getIpv4Data() - { - return array( - array(true, '192.168.1.1', '192.168.1.1'), - array(true, '192.168.1.1', '192.168.1.1/1'), - array(true, '192.168.1.1', '192.168.1.0/24'), - array(false, '192.168.1.1', '1.2.3.4/1'), - array(false, '192.168.1.1', '192.168.1.1/33'), // invalid subnet - array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')), - array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')), - array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')), - array(true, '1.2.3.4', '0.0.0.0/0'), - array(true, '1.2.3.4', '192.168.1.0/0'), - array(false, '1.2.3.4', '256.256.256/0'), // invalid CIDR notation - array(false, 'an_invalid_ip', '192.168.1.0/24'), - ); - } - - /** - * @dataProvider getIpv6Data - */ - public function testIpv6($matches, $remoteAddr, $cidr) - { - if (!\defined('AF_INET6')) { - $this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".'); - } - - $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr)); - } - - public function getIpv6Data() - { - return array( - array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), - array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), - array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'), - array(true, '0:0:0:0:0:0:0:1', '::1'), - array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'), - array(true, '0:0:603:0:396e:4789:8e99:0001', '::/0'), - array(true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'), - array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')), - array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')), - array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')), - array(false, '}__test|O:21:"JDatabaseDriverMysqli":3:{s:2', '::1'), - array(false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'), - ); - } - - /** - * @expectedException \RuntimeException - * @requires extension sockets - */ - public function testAnIpv6WithOptionDisabledIpv6() - { - if (\defined('AF_INET6')) { - $this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".'); - } - - IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'); - } - - /** - * @dataProvider invalidIpAddressData - */ - public function testInvalidIpAddressesDoNotMatch($requestIp, $proxyIp) - { - $this->assertFalse(IpUtils::checkIp4($requestIp, $proxyIp)); - } - - public function invalidIpAddressData() - { - return array( - 'invalid proxy wildcard' => array('192.168.20.13', '*'), - 'invalid proxy missing netmask' => array('192.168.20.13', '0.0.0.0'), - 'invalid request IP with invalid proxy wildcard' => array('0.0.0.0', '*'), - ); - } -} diff --git a/vendor/symfony/http-foundation/Tests/JsonResponseTest.php b/vendor/symfony/http-foundation/Tests/JsonResponseTest.php deleted file mode 100644 index 6687fde..0000000 --- a/vendor/symfony/http-foundation/Tests/JsonResponseTest.php +++ /dev/null @@ -1,266 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\JsonResponse; - -class JsonResponseTest extends TestCase -{ - protected function setUp() - { - parent::setUp(); - - if (!\defined('HHVM_VERSION')) { - $this->iniSet('serialize_precision', 14); - } - } - - public function testConstructorEmptyCreatesJsonObject() - { - $response = new JsonResponse(); - $this->assertSame('{}', $response->getContent()); - } - - public function testConstructorWithArrayCreatesJsonArray() - { - $response = new JsonResponse(array(0, 1, 2, 3)); - $this->assertSame('[0,1,2,3]', $response->getContent()); - } - - public function testConstructorWithAssocArrayCreatesJsonObject() - { - $response = new JsonResponse(array('foo' => 'bar')); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - } - - public function testConstructorWithSimpleTypes() - { - $response = new JsonResponse('foo'); - $this->assertSame('"foo"', $response->getContent()); - - $response = new JsonResponse(0); - $this->assertSame('0', $response->getContent()); - - $response = new JsonResponse(0.1); - $this->assertSame('0.1', $response->getContent()); - - $response = new JsonResponse(true); - $this->assertSame('true', $response->getContent()); - } - - public function testConstructorWithCustomStatus() - { - $response = new JsonResponse(array(), 202); - $this->assertSame(202, $response->getStatusCode()); - } - - public function testConstructorAddsContentTypeHeader() - { - $response = new JsonResponse(); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - } - - public function testConstructorWithCustomHeaders() - { - $response = new JsonResponse(array(), 200, array('ETag' => 'foo')); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - $this->assertSame('foo', $response->headers->get('ETag')); - } - - public function testConstructorWithCustomContentType() - { - $headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json'); - - $response = new JsonResponse(array(), 200, $headers); - $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type')); - } - - public function testSetJson() - { - $response = new JsonResponse('1', 200, array(), true); - $this->assertEquals('1', $response->getContent()); - - $response = new JsonResponse('[1]', 200, array(), true); - $this->assertEquals('[1]', $response->getContent()); - - $response = new JsonResponse(null, 200, array()); - $response->setJson('true'); - $this->assertEquals('true', $response->getContent()); - } - - public function testCreate() - { - $response = JsonResponse::create(array('foo' => 'bar'), 204); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertEquals('{"foo":"bar"}', $response->getContent()); - $this->assertEquals(204, $response->getStatusCode()); - } - - public function testStaticCreateEmptyJsonObject() - { - $response = JsonResponse::create(); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('{}', $response->getContent()); - } - - public function testStaticCreateJsonArray() - { - $response = JsonResponse::create(array(0, 1, 2, 3)); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('[0,1,2,3]', $response->getContent()); - } - - public function testStaticCreateJsonObject() - { - $response = JsonResponse::create(array('foo' => 'bar')); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - } - - public function testStaticCreateWithSimpleTypes() - { - $response = JsonResponse::create('foo'); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('"foo"', $response->getContent()); - - $response = JsonResponse::create(0); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('0', $response->getContent()); - - $response = JsonResponse::create(0.1); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('0.1', $response->getContent()); - - $response = JsonResponse::create(true); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); - $this->assertSame('true', $response->getContent()); - } - - public function testStaticCreateWithCustomStatus() - { - $response = JsonResponse::create(array(), 202); - $this->assertSame(202, $response->getStatusCode()); - } - - public function testStaticCreateAddsContentTypeHeader() - { - $response = JsonResponse::create(); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - } - - public function testStaticCreateWithCustomHeaders() - { - $response = JsonResponse::create(array(), 200, array('ETag' => 'foo')); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - $this->assertSame('foo', $response->headers->get('ETag')); - } - - public function testStaticCreateWithCustomContentType() - { - $headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json'); - - $response = JsonResponse::create(array(), 200, $headers); - $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type')); - } - - public function testSetCallback() - { - $response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback'); - - $this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent()); - $this->assertEquals('text/javascript', $response->headers->get('Content-Type')); - } - - public function testJsonEncodeFlags() - { - $response = new JsonResponse('<>\'&"'); - - $this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent()); - } - - public function testGetEncodingOptions() - { - $response = new JsonResponse(); - - $this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions()); - } - - public function testSetEncodingOptions() - { - $response = new JsonResponse(); - $response->setData(array(array(1, 2, 3))); - - $this->assertEquals('[[1,2,3]]', $response->getContent()); - - $response->setEncodingOptions(JSON_FORCE_OBJECT); - - $this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent()); - } - - public function testItAcceptsJsonAsString() - { - $response = JsonResponse::fromJsonString('{"foo":"bar"}'); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testSetCallbackInvalidIdentifier() - { - $response = new JsonResponse('foo'); - $response->setCallback('+invalid'); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testSetContent() - { - JsonResponse::create("\xB1\x31"); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage This error is expected - */ - public function testSetContentJsonSerializeError() - { - if (!interface_exists('JsonSerializable', false)) { - $this->markTestSkipped('JsonSerializable is required.'); - } - - $serializable = new JsonSerializableObject(); - - JsonResponse::create($serializable); - } - - public function testSetComplexCallback() - { - $response = JsonResponse::create(array('foo' => 'bar')); - $response->setCallback('ಠ_ಠ["foo"].bar[0]'); - - $this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent()); - } -} - -if (interface_exists('JsonSerializable', false)) { - class JsonSerializableObject implements \JsonSerializable - { - public function jsonSerialize() - { - throw new \Exception('This error is expected'); - } - } -} diff --git a/vendor/symfony/http-foundation/Tests/ParameterBagTest.php b/vendor/symfony/http-foundation/Tests/ParameterBagTest.php deleted file mode 100644 index dccfd4f..0000000 --- a/vendor/symfony/http-foundation/Tests/ParameterBagTest.php +++ /dev/null @@ -1,194 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\ParameterBag; - -class ParameterBagTest extends TestCase -{ - public function testConstructor() - { - $this->testAll(); - } - - public function testAll() - { - $bag = new ParameterBag(array('foo' => 'bar')); - $this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input'); - } - - public function testKeys() - { - $bag = new ParameterBag(array('foo' => 'bar')); - $this->assertEquals(array('foo'), $bag->keys()); - } - - public function testAdd() - { - $bag = new ParameterBag(array('foo' => 'bar')); - $bag->add(array('bar' => 'bas')); - $this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all()); - } - - public function testRemove() - { - $bag = new ParameterBag(array('foo' => 'bar')); - $bag->add(array('bar' => 'bas')); - $this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all()); - $bag->remove('bar'); - $this->assertEquals(array('foo' => 'bar'), $bag->all()); - } - - public function testReplace() - { - $bag = new ParameterBag(array('foo' => 'bar')); - - $bag->replace(array('FOO' => 'BAR')); - $this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument'); - $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input'); - } - - public function testGet() - { - $bag = new ParameterBag(array('foo' => 'bar', 'null' => null)); - - $this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter'); - $this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined'); - $this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set'); - } - - public function testGetDoesNotUseDeepByDefault() - { - $bag = new ParameterBag(array('foo' => array('bar' => 'moo'))); - - $this->assertNull($bag->get('foo[bar]')); - } - - public function testSet() - { - $bag = new ParameterBag(array()); - - $bag->set('foo', 'bar'); - $this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter'); - - $bag->set('foo', 'baz'); - $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter'); - } - - public function testHas() - { - $bag = new ParameterBag(array('foo' => 'bar')); - - $this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined'); - $this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined'); - } - - public function testGetAlpha() - { - $bag = new ParameterBag(array('word' => 'foo_BAR_012')); - - $this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters'); - $this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined'); - } - - public function testGetAlnum() - { - $bag = new ParameterBag(array('word' => 'foo_BAR_012')); - - $this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters'); - $this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined'); - } - - public function testGetDigits() - { - $bag = new ParameterBag(array('word' => 'foo_BAR_012')); - - $this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string'); - $this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined'); - } - - public function testGetInt() - { - $bag = new ParameterBag(array('digits' => '0123')); - - $this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer'); - $this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined'); - } - - public function testFilter() - { - $bag = new ParameterBag(array( - 'digits' => '0123ab', - 'email' => 'example@example.com', - 'url' => 'http://example.com/foo', - 'dec' => '256', - 'hex' => '0x100', - 'array' => array('bang'), - )); - - $this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found'); - - $this->assertEquals('0123', $bag->filter('digits', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters'); - - $this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email'); - - $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as URL with a path'); - - // This test is repeated for code-coverage - $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path'); - - $this->assertFalse($bag->filter('dec', '', FILTER_VALIDATE_INT, array( - 'flags' => FILTER_FLAG_ALLOW_HEX, - 'options' => array('min_range' => 1, 'max_range' => 0xff), - )), '->filter() gets a value of parameter as integer between boundaries'); - - $this->assertFalse($bag->filter('hex', '', FILTER_VALIDATE_INT, array( - 'flags' => FILTER_FLAG_ALLOW_HEX, - 'options' => array('min_range' => 1, 'max_range' => 0xff), - )), '->filter() gets a value of parameter as integer between boundaries'); - - $this->assertEquals(array('bang'), $bag->filter('array', ''), '->filter() gets a value of parameter as an array'); - } - - public function testGetIterator() - { - $parameters = array('foo' => 'bar', 'hello' => 'world'); - $bag = new ParameterBag($parameters); - - $i = 0; - foreach ($bag as $key => $val) { - ++$i; - $this->assertEquals($parameters[$key], $val); - } - - $this->assertEquals(\count($parameters), $i); - } - - public function testCount() - { - $parameters = array('foo' => 'bar', 'hello' => 'world'); - $bag = new ParameterBag($parameters); - - $this->assertCount(\count($parameters), $bag); - } - - public function testGetBoolean() - { - $parameters = array('string_true' => 'true', 'string_false' => 'false'); - $bag = new ParameterBag($parameters); - - $this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true'); - $this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false'); - $this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined'); - } -} diff --git a/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php b/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php deleted file mode 100644 index d389e83..0000000 --- a/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\RedirectResponse; - -class RedirectResponseTest extends TestCase -{ - public function testGenerateMetaRedirect() - { - $response = new RedirectResponse('foo.bar'); - - $this->assertEquals(1, preg_match( - '##', - preg_replace(array('/\s+/', '/\'/'), array(' ', '"'), $response->getContent()) - )); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testRedirectResponseConstructorNullUrl() - { - $response = new RedirectResponse(null); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testRedirectResponseConstructorWrongStatusCode() - { - $response = new RedirectResponse('foo.bar', 404); - } - - public function testGenerateLocationHeader() - { - $response = new RedirectResponse('foo.bar'); - - $this->assertTrue($response->headers->has('Location')); - $this->assertEquals('foo.bar', $response->headers->get('Location')); - } - - public function testGetTargetUrl() - { - $response = new RedirectResponse('foo.bar'); - - $this->assertEquals('foo.bar', $response->getTargetUrl()); - } - - public function testSetTargetUrl() - { - $response = new RedirectResponse('foo.bar'); - $response->setTargetUrl('baz.beep'); - - $this->assertEquals('baz.beep', $response->getTargetUrl()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testSetTargetUrlNull() - { - $response = new RedirectResponse('foo.bar'); - $response->setTargetUrl(null); - } - - public function testCreate() - { - $response = RedirectResponse::create('foo', 301); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertEquals(301, $response->getStatusCode()); - } - - public function testCacheHeaders() - { - $response = new RedirectResponse('foo.bar', 301); - $this->assertFalse($response->headers->hasCacheControlDirective('no-cache')); - - $response = new RedirectResponse('foo.bar', 301, array('cache-control' => 'max-age=86400')); - $this->assertFalse($response->headers->hasCacheControlDirective('no-cache')); - $this->assertTrue($response->headers->hasCacheControlDirective('max-age')); - - $response = new RedirectResponse('foo.bar', 302); - $this->assertTrue($response->headers->hasCacheControlDirective('no-cache')); - } -} diff --git a/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php b/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php deleted file mode 100644 index 10d764a..0000000 --- a/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestMatcher; - -class RequestMatcherTest extends TestCase -{ - /** - * @dataProvider getMethodData - */ - public function testMethod($requestMethod, $matcherMethod, $isMatch) - { - $matcher = new RequestMatcher(); - $matcher->matchMethod($matcherMethod); - $request = Request::create('', $requestMethod); - $this->assertSame($isMatch, $matcher->matches($request)); - - $matcher = new RequestMatcher(null, null, $matcherMethod); - $request = Request::create('', $requestMethod); - $this->assertSame($isMatch, $matcher->matches($request)); - } - - public function getMethodData() - { - return array( - array('get', 'get', true), - array('get', array('get', 'post'), true), - array('get', 'post', false), - array('get', 'GET', true), - array('get', array('GET', 'POST'), true), - array('get', 'POST', false), - ); - } - - public function testScheme() - { - $httpRequest = $request = $request = Request::create(''); - $httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on')); - - $matcher = new RequestMatcher(); - $matcher->matchScheme('https'); - $this->assertFalse($matcher->matches($httpRequest)); - $this->assertTrue($matcher->matches($httpsRequest)); - - $matcher->matchScheme('http'); - $this->assertFalse($matcher->matches($httpsRequest)); - $this->assertTrue($matcher->matches($httpRequest)); - - $matcher = new RequestMatcher(); - $this->assertTrue($matcher->matches($httpsRequest)); - $this->assertTrue($matcher->matches($httpRequest)); - } - - /** - * @dataProvider getHostData - */ - public function testHost($pattern, $isMatch) - { - $matcher = new RequestMatcher(); - $request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com')); - - $matcher->matchHost($pattern); - $this->assertSame($isMatch, $matcher->matches($request)); - - $matcher = new RequestMatcher(null, $pattern); - $this->assertSame($isMatch, $matcher->matches($request)); - } - - public function getHostData() - { - return array( - array('.*\.example\.com', true), - array('\.example\.com$', true), - array('^.*\.example\.com$', true), - array('.*\.sensio\.com', false), - array('.*\.example\.COM', true), - array('\.example\.COM$', true), - array('^.*\.example\.COM$', true), - array('.*\.sensio\.COM', false), - ); - } - - public function testPath() - { - $matcher = new RequestMatcher(); - - $request = Request::create('/admin/foo'); - - $matcher->matchPath('/admin/.*'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchPath('/admin'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchPath('^/admin/.*$'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchMethod('/blog/.*'); - $this->assertFalse($matcher->matches($request)); - } - - public function testPathWithLocaleIsNotSupported() - { - $matcher = new RequestMatcher(); - $request = Request::create('/en/login'); - $request->setLocale('en'); - - $matcher->matchPath('^/{_locale}/login$'); - $this->assertFalse($matcher->matches($request)); - } - - public function testPathWithEncodedCharacters() - { - $matcher = new RequestMatcher(); - $request = Request::create('/admin/fo%20o'); - $matcher->matchPath('^/admin/fo o*$'); - $this->assertTrue($matcher->matches($request)); - } - - public function testAttributes() - { - $matcher = new RequestMatcher(); - - $request = Request::create('/admin/foo'); - $request->attributes->set('foo', 'foo_bar'); - - $matcher->matchAttribute('foo', 'foo_.*'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchAttribute('foo', 'foo'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchAttribute('foo', '^foo_bar$'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchAttribute('foo', 'babar'); - $this->assertFalse($matcher->matches($request)); - } -} diff --git a/vendor/symfony/http-foundation/Tests/RequestStackTest.php b/vendor/symfony/http-foundation/Tests/RequestStackTest.php deleted file mode 100644 index a84fb26..0000000 --- a/vendor/symfony/http-foundation/Tests/RequestStackTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; - -class RequestStackTest extends TestCase -{ - public function testGetCurrentRequest() - { - $requestStack = new RequestStack(); - $this->assertNull($requestStack->getCurrentRequest()); - - $request = Request::create('/foo'); - - $requestStack->push($request); - $this->assertSame($request, $requestStack->getCurrentRequest()); - - $this->assertSame($request, $requestStack->pop()); - $this->assertNull($requestStack->getCurrentRequest()); - - $this->assertNull($requestStack->pop()); - } - - public function testGetMasterRequest() - { - $requestStack = new RequestStack(); - $this->assertNull($requestStack->getMasterRequest()); - - $masterRequest = Request::create('/foo'); - $subRequest = Request::create('/bar'); - - $requestStack->push($masterRequest); - $requestStack->push($subRequest); - - $this->assertSame($masterRequest, $requestStack->getMasterRequest()); - } - - public function testGetParentRequest() - { - $requestStack = new RequestStack(); - $this->assertNull($requestStack->getParentRequest()); - - $masterRequest = Request::create('/foo'); - - $requestStack->push($masterRequest); - $this->assertNull($requestStack->getParentRequest()); - - $firstSubRequest = Request::create('/bar'); - - $requestStack->push($firstSubRequest); - $this->assertSame($masterRequest, $requestStack->getParentRequest()); - - $secondSubRequest = Request::create('/baz'); - - $requestStack->push($secondSubRequest); - $this->assertSame($firstSubRequest, $requestStack->getParentRequest()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/RequestTest.php b/vendor/symfony/http-foundation/Tests/RequestTest.php deleted file mode 100644 index f2c8f94..0000000 --- a/vendor/symfony/http-foundation/Tests/RequestTest.php +++ /dev/null @@ -1,2446 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; - -class RequestTest extends TestCase -{ - protected function tearDown() - { - Request::setTrustedProxies(array(), -1); - Request::setTrustedHosts(array()); - } - - public function testInitialize() - { - $request = new Request(); - - $request->initialize(array('foo' => 'bar')); - $this->assertEquals('bar', $request->query->get('foo'), '->initialize() takes an array of query parameters as its first argument'); - - $request->initialize(array(), array('foo' => 'bar')); - $this->assertEquals('bar', $request->request->get('foo'), '->initialize() takes an array of request parameters as its second argument'); - - $request->initialize(array(), array(), array('foo' => 'bar')); - $this->assertEquals('bar', $request->attributes->get('foo'), '->initialize() takes an array of attributes as its third argument'); - - $request->initialize(array(), array(), array(), array(), array(), array('HTTP_FOO' => 'bar')); - $this->assertEquals('bar', $request->headers->get('FOO'), '->initialize() takes an array of HTTP headers as its sixth argument'); - } - - public function testGetLocale() - { - $request = new Request(); - $request->setLocale('pl'); - $locale = $request->getLocale(); - $this->assertEquals('pl', $locale); - } - - public function testGetUser() - { - $request = Request::create('http://user:password@test.com'); - $user = $request->getUser(); - - $this->assertEquals('user', $user); - } - - public function testGetPassword() - { - $request = Request::create('http://user:password@test.com'); - $password = $request->getPassword(); - - $this->assertEquals('password', $password); - } - - public function testIsNoCache() - { - $request = new Request(); - $isNoCache = $request->isNoCache(); - - $this->assertFalse($isNoCache); - } - - public function testGetContentType() - { - $request = new Request(); - $contentType = $request->getContentType(); - - $this->assertNull($contentType); - } - - public function testSetDefaultLocale() - { - $request = new Request(); - $request->setDefaultLocale('pl'); - $locale = $request->getLocale(); - - $this->assertEquals('pl', $locale); - } - - public function testCreate() - { - $request = Request::create('http://test.com/foo?bar=baz'); - $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('bar=baz', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com/foo', 'GET', array('bar' => 'baz')); - $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('bar=baz', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz')); - $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('bar=baz', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('https://test.com/foo?bar=baz'); - $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('bar=baz', $request->getQueryString()); - $this->assertEquals(443, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertTrue($request->isSecure()); - - $request = Request::create('test.com:90/foo'); - $this->assertEquals('http://test.com:90/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('test.com', $request->getHost()); - $this->assertEquals('test.com:90', $request->getHttpHost()); - $this->assertEquals(90, $request->getPort()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('https://test.com:90/foo'); - $this->assertEquals('https://test.com:90/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('test.com', $request->getHost()); - $this->assertEquals('test.com:90', $request->getHttpHost()); - $this->assertEquals(90, $request->getPort()); - $this->assertTrue($request->isSecure()); - - $request = Request::create('https://127.0.0.1:90/foo'); - $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('127.0.0.1', $request->getHost()); - $this->assertEquals('127.0.0.1:90', $request->getHttpHost()); - $this->assertEquals(90, $request->getPort()); - $this->assertTrue($request->isSecure()); - - $request = Request::create('https://[::1]:90/foo'); - $this->assertEquals('https://[::1]:90/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('[::1]', $request->getHost()); - $this->assertEquals('[::1]:90', $request->getHttpHost()); - $this->assertEquals(90, $request->getPort()); - $this->assertTrue($request->isSecure()); - - $request = Request::create('https://[::1]/foo'); - $this->assertEquals('https://[::1]/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('[::1]', $request->getHost()); - $this->assertEquals('[::1]', $request->getHttpHost()); - $this->assertEquals(443, $request->getPort()); - $this->assertTrue($request->isSecure()); - - $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; - $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json); - $this->assertEquals($json, $request->getContent()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com'); - $this->assertEquals('http://test.com/', $request->getUri()); - $this->assertEquals('/', $request->getPathInfo()); - $this->assertEquals('', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com?test=1'); - $this->assertEquals('http://test.com/?test=1', $request->getUri()); - $this->assertEquals('/', $request->getPathInfo()); - $this->assertEquals('test=1', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com:90/?test=1'); - $this->assertEquals('http://test.com:90/?test=1', $request->getUri()); - $this->assertEquals('/', $request->getPathInfo()); - $this->assertEquals('test=1', $request->getQueryString()); - $this->assertEquals(90, $request->getPort()); - $this->assertEquals('test.com:90', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://username:password@test.com'); - $this->assertEquals('http://test.com/', $request->getUri()); - $this->assertEquals('/', $request->getPathInfo()); - $this->assertEquals('', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertEquals('username', $request->getUser()); - $this->assertEquals('password', $request->getPassword()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://username@test.com'); - $this->assertEquals('http://test.com/', $request->getUri()); - $this->assertEquals('/', $request->getPathInfo()); - $this->assertEquals('', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertEquals('username', $request->getUser()); - $this->assertSame('', $request->getPassword()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com/?foo'); - $this->assertEquals('/?foo', $request->getRequestUri()); - $this->assertEquals(array('foo' => ''), $request->query->all()); - - // assume rewrite rule: (.*) --> app/app.php; app/ is a symlink to a symfony web/ directory - $request = Request::create('http://test.com/apparthotel-1234', 'GET', array(), array(), array(), - array( - 'DOCUMENT_ROOT' => '/var/www/www.test.com', - 'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php', - 'SCRIPT_NAME' => '/app/app.php', - 'PHP_SELF' => '/app/app.php/apparthotel-1234', - )); - $this->assertEquals('http://test.com/apparthotel-1234', $request->getUri()); - $this->assertEquals('/apparthotel-1234', $request->getPathInfo()); - $this->assertEquals('', $request->getQueryString()); - $this->assertEquals(80, $request->getPort()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertFalse($request->isSecure()); - - // Fragment should not be included in the URI - $request = Request::create('http://test.com/foo#bar'); - $this->assertEquals('http://test.com/foo', $request->getUri()); - } - - public function testCreateWithRequestUri() - { - $request = Request::create('http://test.com:80/foo'); - $request->server->set('REQUEST_URI', 'http://test.com:80/foo'); - $this->assertEquals('http://test.com/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('test.com', $request->getHost()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com:8080/foo'); - $request->server->set('REQUEST_URI', 'http://test.com:8080/foo'); - $this->assertEquals('http://test.com:8080/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('test.com', $request->getHost()); - $this->assertEquals('test.com:8080', $request->getHttpHost()); - $this->assertEquals(8080, $request->getPort()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz')); - $request->server->set('REQUEST_URI', 'http://test.com/foo?bar=foo'); - $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('bar=baz', $request->getQueryString()); - $this->assertEquals('test.com', $request->getHost()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - $request = Request::create('https://test.com:443/foo'); - $request->server->set('REQUEST_URI', 'https://test.com:443/foo'); - $this->assertEquals('https://test.com/foo', $request->getUri()); - $this->assertEquals('/foo', $request->getPathInfo()); - $this->assertEquals('test.com', $request->getHost()); - $this->assertEquals('test.com', $request->getHttpHost()); - $this->assertEquals(443, $request->getPort()); - $this->assertTrue($request->isSecure()); - - // Fragment should not be included in the URI - $request = Request::create('http://test.com/foo#bar'); - $request->server->set('REQUEST_URI', 'http://test.com/foo#bar'); - $this->assertEquals('http://test.com/foo', $request->getUri()); - } - - /** - * @dataProvider getRequestUriData - */ - public function testGetRequestUri($serverRequestUri, $expected, $message) - { - $request = new Request(); - $request->server->add(array( - 'REQUEST_URI' => $serverRequestUri, - - // For having http://test.com - 'SERVER_NAME' => 'test.com', - 'SERVER_PORT' => 80, - )); - - $this->assertSame($expected, $request->getRequestUri(), $message); - $this->assertSame($expected, $request->server->get('REQUEST_URI'), 'Normalize the request URI.'); - } - - public function getRequestUriData() - { - $message = 'Do not modify the path.'; - yield array('/foo', '/foo', $message); - yield array('//bar/foo', '//bar/foo', $message); - yield array('///bar/foo', '///bar/foo', $message); - - $message = 'Handle when the scheme, host are on REQUEST_URI.'; - yield array('http://test.com/foo?bar=baz', '/foo?bar=baz', $message); - - $message = 'Handle when the scheme, host and port are on REQUEST_URI.'; - yield array('http://test.com:80/foo', '/foo', $message); - yield array('https://test.com:8080/foo', '/foo', $message); - yield array('https://test.com:443/foo', '/foo', $message); - - $message = 'Fragment should not be included in the URI'; - yield array('http://test.com/foo#bar', '/foo', $message); - yield array('/foo#bar', '/foo', $message); - } - - public function testGetRequestUriWithoutRequiredHeader() - { - $expected = ''; - - $request = new Request(); - - $message = 'Fallback to empty URI when headers are missing.'; - $this->assertSame($expected, $request->getRequestUri(), $message); - $this->assertSame($expected, $request->server->get('REQUEST_URI'), 'Normalize the request URI.'); - } - - public function testCreateCheckPrecedence() - { - // server is used by default - $request = Request::create('/', 'DELETE', array(), array(), array(), array( - 'HTTP_HOST' => 'example.com', - 'HTTPS' => 'on', - 'SERVER_PORT' => 443, - 'PHP_AUTH_USER' => 'fabien', - 'PHP_AUTH_PW' => 'pa$$', - 'QUERY_STRING' => 'foo=bar', - 'CONTENT_TYPE' => 'application/json', - )); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(443, $request->getPort()); - $this->assertTrue($request->isSecure()); - $this->assertEquals('fabien', $request->getUser()); - $this->assertEquals('pa$$', $request->getPassword()); - $this->assertEquals('', $request->getQueryString()); - $this->assertEquals('application/json', $request->headers->get('CONTENT_TYPE')); - - // URI has precedence over server - $request = Request::create('http://thomas:pokemon@example.net:8080/?foo=bar', 'GET', array(), array(), array(), array( - 'HTTP_HOST' => 'example.com', - 'HTTPS' => 'on', - 'SERVER_PORT' => 443, - )); - $this->assertEquals('example.net', $request->getHost()); - $this->assertEquals(8080, $request->getPort()); - $this->assertFalse($request->isSecure()); - $this->assertEquals('thomas', $request->getUser()); - $this->assertEquals('pokemon', $request->getPassword()); - $this->assertEquals('foo=bar', $request->getQueryString()); - } - - public function testDuplicate() - { - $request = new Request(array('foo' => 'bar'), array('foo' => 'bar'), array('foo' => 'bar'), array(), array(), array('HTTP_FOO' => 'bar')); - $dup = $request->duplicate(); - - $this->assertEquals($request->query->all(), $dup->query->all(), '->duplicate() duplicates a request an copy the current query parameters'); - $this->assertEquals($request->request->all(), $dup->request->all(), '->duplicate() duplicates a request an copy the current request parameters'); - $this->assertEquals($request->attributes->all(), $dup->attributes->all(), '->duplicate() duplicates a request an copy the current attributes'); - $this->assertEquals($request->headers->all(), $dup->headers->all(), '->duplicate() duplicates a request an copy the current HTTP headers'); - - $dup = $request->duplicate(array('foo' => 'foobar'), array('foo' => 'foobar'), array('foo' => 'foobar'), array(), array(), array('HTTP_FOO' => 'foobar')); - - $this->assertEquals(array('foo' => 'foobar'), $dup->query->all(), '->duplicate() overrides the query parameters if provided'); - $this->assertEquals(array('foo' => 'foobar'), $dup->request->all(), '->duplicate() overrides the request parameters if provided'); - $this->assertEquals(array('foo' => 'foobar'), $dup->attributes->all(), '->duplicate() overrides the attributes if provided'); - $this->assertEquals(array('foo' => array('foobar')), $dup->headers->all(), '->duplicate() overrides the HTTP header if provided'); - } - - public function testDuplicateWithFormat() - { - $request = new Request(array(), array(), array('_format' => 'json')); - $dup = $request->duplicate(); - - $this->assertEquals('json', $dup->getRequestFormat()); - $this->assertEquals('json', $dup->attributes->get('_format')); - - $request = new Request(); - $request->setRequestFormat('xml'); - $dup = $request->duplicate(); - - $this->assertEquals('xml', $dup->getRequestFormat()); - } - - /** - * @dataProvider getFormatToMimeTypeMapProviderWithAdditionalNullFormat - */ - public function testGetFormatFromMimeType($format, $mimeTypes) - { - $request = new Request(); - foreach ($mimeTypes as $mime) { - $this->assertEquals($format, $request->getFormat($mime)); - } - $request->setFormat($format, $mimeTypes); - foreach ($mimeTypes as $mime) { - $this->assertEquals($format, $request->getFormat($mime)); - - if (null !== $format) { - $this->assertEquals($mimeTypes[0], $request->getMimeType($format)); - } - } - } - - public function getFormatToMimeTypeMapProviderWithAdditionalNullFormat() - { - return array_merge( - array(array(null, array(null, 'unexistent-mime-type'))), - $this->getFormatToMimeTypeMapProvider() - ); - } - - public function testGetFormatFromMimeTypeWithParameters() - { - $request = new Request(); - $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8')); - $this->assertEquals('json', $request->getFormat('application/json;charset=utf-8')); - $this->assertEquals('json', $request->getFormat('application/json ; charset=utf-8')); - $this->assertEquals('json', $request->getFormat('application/json ;charset=utf-8')); - } - - /** - * @dataProvider getFormatToMimeTypeMapProvider - */ - public function testGetMimeTypeFromFormat($format, $mimeTypes) - { - $request = new Request(); - $this->assertEquals($mimeTypes[0], $request->getMimeType($format)); - } - - /** - * @dataProvider getFormatToMimeTypeMapProvider - */ - public function testGetMimeTypesFromFormat($format, $mimeTypes) - { - $this->assertEquals($mimeTypes, Request::getMimeTypes($format)); - } - - public function testGetMimeTypesFromInexistentFormat() - { - $request = new Request(); - $this->assertNull($request->getMimeType('foo')); - $this->assertEquals(array(), Request::getMimeTypes('foo')); - } - - public function testGetFormatWithCustomMimeType() - { - $request = new Request(); - $request->setFormat('custom', 'application/vnd.foo.api;myversion=2.3'); - $this->assertEquals('custom', $request->getFormat('application/vnd.foo.api;myversion=2.3')); - } - - public function getFormatToMimeTypeMapProvider() - { - return array( - array('txt', array('text/plain')), - array('js', array('application/javascript', 'application/x-javascript', 'text/javascript')), - array('css', array('text/css')), - array('json', array('application/json', 'application/x-json')), - array('jsonld', array('application/ld+json')), - array('xml', array('text/xml', 'application/xml', 'application/x-xml')), - array('rdf', array('application/rdf+xml')), - array('atom', array('application/atom+xml')), - ); - } - - public function testGetUri() - { - $server = array(); - - // Standard Request on non default PORT - // http://host:8080/index.php/path/info?query=string - - $server['HTTP_HOST'] = 'host:8080'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '8080'; - - $server['QUERY_STRING'] = 'query=string'; - $server['REQUEST_URI'] = '/index.php/path/info?query=string'; - $server['SCRIPT_NAME'] = '/index.php'; - $server['PATH_INFO'] = '/path/info'; - $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info'; - $server['PHP_SELF'] = '/index_dev.php/path/info'; - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - - $request = new Request(); - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://host:8080/index.php/path/info?query=string', $request->getUri(), '->getUri() with non default port'); - - // Use std port number - $server['HTTP_HOST'] = 'host'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://host/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port'); - - // Without HOST HEADER - unset($server['HTTP_HOST']); - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://servername/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port without HOST_HEADER'); - - // Request with URL REWRITING (hide index.php) - // RewriteCond %{REQUEST_FILENAME} !-f - // RewriteRule ^(.*)$ index.php [QSA,L] - // http://host:8080/path/info?query=string - $server = array(); - $server['HTTP_HOST'] = 'host:8080'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '8080'; - - $server['REDIRECT_QUERY_STRING'] = 'query=string'; - $server['REDIRECT_URL'] = '/path/info'; - $server['SCRIPT_NAME'] = '/index.php'; - $server['QUERY_STRING'] = 'query=string'; - $server['REQUEST_URI'] = '/path/info?toto=test&1=1'; - $server['SCRIPT_NAME'] = '/index.php'; - $server['PHP_SELF'] = '/index.php'; - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://host:8080/path/info?query=string', $request->getUri(), '->getUri() with rewrite'); - - // Use std port number - // http://host/path/info?query=string - $server['HTTP_HOST'] = 'host'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://host/path/info?query=string', $request->getUri(), '->getUri() with rewrite and default port'); - - // Without HOST HEADER - unset($server['HTTP_HOST']); - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://servername/path/info?query=string', $request->getUri(), '->getUri() with rewrite, default port without HOST_HEADER'); - - // With encoded characters - - $server = array( - 'HTTP_HOST' => 'host:8080', - 'SERVER_NAME' => 'servername', - 'SERVER_PORT' => '8080', - 'QUERY_STRING' => 'query=string', - 'REQUEST_URI' => '/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', - 'SCRIPT_NAME' => '/ba se/index_dev.php', - 'PATH_TRANSLATED' => 'redirect:/index.php/foo bar/in+fo', - 'PHP_SELF' => '/ba se/index_dev.php/path/info', - 'SCRIPT_FILENAME' => '/some/where/ba se/index_dev.php', - ); - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals( - 'http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', - $request->getUri() - ); - - // with user info - - $server['PHP_AUTH_USER'] = 'fabien'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri()); - - $server['PHP_AUTH_PW'] = 'symfony'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri()); - } - - public function testGetUriForPath() - { - $request = Request::create('http://test.com/foo?bar=baz'); - $this->assertEquals('http://test.com/some/path', $request->getUriForPath('/some/path')); - - $request = Request::create('http://test.com:90/foo?bar=baz'); - $this->assertEquals('http://test.com:90/some/path', $request->getUriForPath('/some/path')); - - $request = Request::create('https://test.com/foo?bar=baz'); - $this->assertEquals('https://test.com/some/path', $request->getUriForPath('/some/path')); - - $request = Request::create('https://test.com:90/foo?bar=baz'); - $this->assertEquals('https://test.com:90/some/path', $request->getUriForPath('/some/path')); - - $server = array(); - - // Standard Request on non default PORT - // http://host:8080/index.php/path/info?query=string - - $server['HTTP_HOST'] = 'host:8080'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '8080'; - - $server['QUERY_STRING'] = 'query=string'; - $server['REQUEST_URI'] = '/index.php/path/info?query=string'; - $server['SCRIPT_NAME'] = '/index.php'; - $server['PATH_INFO'] = '/path/info'; - $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info'; - $server['PHP_SELF'] = '/index_dev.php/path/info'; - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - - $request = new Request(); - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://host:8080/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with non default port'); - - // Use std port number - $server['HTTP_HOST'] = 'host'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://host/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port'); - - // Without HOST HEADER - unset($server['HTTP_HOST']); - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://servername/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port without HOST_HEADER'); - - // Request with URL REWRITING (hide index.php) - // RewriteCond %{REQUEST_FILENAME} !-f - // RewriteRule ^(.*)$ index.php [QSA,L] - // http://host:8080/path/info?query=string - $server = array(); - $server['HTTP_HOST'] = 'host:8080'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '8080'; - - $server['REDIRECT_QUERY_STRING'] = 'query=string'; - $server['REDIRECT_URL'] = '/path/info'; - $server['SCRIPT_NAME'] = '/index.php'; - $server['QUERY_STRING'] = 'query=string'; - $server['REQUEST_URI'] = '/path/info?toto=test&1=1'; - $server['SCRIPT_NAME'] = '/index.php'; - $server['PHP_SELF'] = '/index.php'; - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://host:8080/some/path', $request->getUriForPath('/some/path'), '->getUri() with rewrite'); - - // Use std port number - // http://host/path/info?query=string - $server['HTTP_HOST'] = 'host'; - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://host/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite and default port'); - - // Without HOST HEADER - unset($server['HTTP_HOST']); - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '80'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite, default port without HOST_HEADER'); - $this->assertEquals('servername', $request->getHttpHost()); - - // with user info - - $server['PHP_AUTH_USER'] = 'fabien'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path')); - - $server['PHP_AUTH_PW'] = 'symfony'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path')); - } - - /** - * @dataProvider getRelativeUriForPathData() - */ - public function testGetRelativeUriForPath($expected, $pathinfo, $path) - { - $this->assertEquals($expected, Request::create($pathinfo)->getRelativeUriForPath($path)); - } - - public function getRelativeUriForPathData() - { - return array( - array('me.png', '/foo', '/me.png'), - array('../me.png', '/foo/bar', '/me.png'), - array('me.png', '/foo/bar', '/foo/me.png'), - array('../baz/me.png', '/foo/bar/b', '/foo/baz/me.png'), - array('../../fooz/baz/me.png', '/foo/bar/b', '/fooz/baz/me.png'), - array('baz/me.png', '/foo/bar/b', 'baz/me.png'), - ); - } - - public function testGetUserInfo() - { - $request = new Request(); - - $server = array('PHP_AUTH_USER' => 'fabien'); - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('fabien', $request->getUserInfo()); - - $server['PHP_AUTH_USER'] = '0'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('0', $request->getUserInfo()); - - $server['PHP_AUTH_PW'] = '0'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('0:0', $request->getUserInfo()); - } - - public function testGetSchemeAndHttpHost() - { - $request = new Request(); - - $server = array(); - $server['SERVER_NAME'] = 'servername'; - $server['SERVER_PORT'] = '90'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); - - $server['PHP_AUTH_USER'] = 'fabien'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); - - $server['PHP_AUTH_USER'] = '0'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); - - $server['PHP_AUTH_PW'] = '0'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); - } - - /** - * @dataProvider getQueryStringNormalizationData - */ - public function testGetQueryString($query, $expectedQuery, $msg) - { - $request = new Request(); - - $request->server->set('QUERY_STRING', $query); - $this->assertSame($expectedQuery, $request->getQueryString(), $msg); - } - - public function getQueryStringNormalizationData() - { - return array( - array('foo', 'foo', 'works with valueless parameters'), - array('foo=', 'foo=', 'includes a dangling equal sign'), - array('bar=&foo=bar', 'bar=&foo=bar', '->works with empty parameters'), - array('foo=bar&bar=', 'bar=&foo=bar', 'sorts keys alphabetically'), - - // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded). - // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. - array('him=John%20Doe&her=Jane+Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes spaces in both encodings "%20" and "+"'), - - array('foo[]=1&foo[]=2', 'foo%5B%5D=1&foo%5B%5D=2', 'allows array notation'), - array('foo=1&foo=2', 'foo=1&foo=2', 'allows repeated parameters'), - array('pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'), - array('0', '0', 'allows "0"'), - array('Jane Doe&John%20Doe', 'Jane%20Doe&John%20Doe', 'normalizes encoding in keys'), - array('her=Jane Doe&him=John%20Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes encoding in values'), - array('foo=bar&&&test&&', 'foo=bar&test', 'removes unneeded delimiters'), - array('formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'), - - // Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway. - // PHP also does not include them when building _GET. - array('foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'), - ); - } - - public function testGetQueryStringReturnsNull() - { - $request = new Request(); - - $this->assertNull($request->getQueryString(), '->getQueryString() returns null for non-existent query string'); - - $request->server->set('QUERY_STRING', ''); - $this->assertNull($request->getQueryString(), '->getQueryString() returns null for empty query string'); - } - - public function testGetHost() - { - $request = new Request(); - - $request->initialize(array('foo' => 'bar')); - $this->assertEquals('', $request->getHost(), '->getHost() return empty string if not initialized'); - - $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com')); - $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header'); - - // Host header with port number - $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com:8080')); - $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header with port number'); - - // Server values - $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com')); - $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from server name'); - - $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com', 'HTTP_HOST' => 'www.host.com')); - $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME '); - } - - public function testGetPort() - { - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => 'https', - 'HTTP_X_FORWARDED_PORT' => '443', - )); - $port = $request->getPort(); - - $this->assertEquals(80, $port, 'Without trusted proxies FORWARDED_PROTO and FORWARDED_PORT are ignored.'); - - Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL); - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => 'https', - 'HTTP_X_FORWARDED_PORT' => '8443', - )); - $this->assertEquals(80, $request->getPort(), 'With PROTO and PORT on untrusted connection server value takes precedence.'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $this->assertEquals(8443, $request->getPort(), 'With PROTO and PORT set PORT takes precedence.'); - - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => 'https', - )); - $this->assertEquals(80, $request->getPort(), 'With only PROTO set getPort() ignores trusted headers on untrusted connection.'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $this->assertEquals(443, $request->getPort(), 'With only PROTO set getPort() defaults to 443.'); - - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => 'http', - )); - $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() ignores trusted headers on untrusted connection.'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() returns port of the original request.'); - - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => 'On', - )); - $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is On, getPort() ignores trusted headers on untrusted connection.'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is On, getPort() defaults to 443.'); - - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => '1', - )); - $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is 1, getPort() ignores trusted headers on untrusted connection.'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is 1, getPort() defaults to 443.'); - - $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( - 'HTTP_X_FORWARDED_PROTO' => 'something-else', - )); - $port = $request->getPort(); - $this->assertEquals(80, $port, 'With only PROTO set and value is not recognized, getPort() defaults to 80.'); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetHostWithFakeHttpHostValue() - { - $request = new Request(); - $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.host.com?query=string')); - $request->getHost(); - } - - public function testGetSetMethod() - { - $request = new Request(); - - $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns GET if no method is defined'); - - $request->setMethod('get'); - $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns an uppercased string'); - - $request->setMethod('PURGE'); - $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method even if it is not a standard one'); - - $request->setMethod('POST'); - $this->assertEquals('POST', $request->getMethod(), '->getMethod() returns the method POST if no _method is defined'); - - $request->setMethod('POST'); - $request->request->set('_method', 'purge'); - $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled'); - - $request = new Request(); - $request->setMethod('POST'); - $request->request->set('_method', 'purge'); - - $this->assertFalse(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be disabled by default'); - - Request::enableHttpMethodParameterOverride(); - - $this->assertTrue(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be enabled now but it is not'); - - $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); - $this->disableHttpMethodParameterOverride(); - - $request = new Request(); - $request->setMethod('POST'); - $request->query->set('_method', 'purge'); - $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled'); - - $request = new Request(); - $request->setMethod('POST'); - $request->query->set('_method', 'purge'); - Request::enableHttpMethodParameterOverride(); - $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); - $this->disableHttpMethodParameterOverride(); - - $request = new Request(); - $request->setMethod('POST'); - $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete'); - $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override even though _method is set if defined and POST'); - - $request = new Request(); - $request->setMethod('POST'); - $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete'); - $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override if defined and POST'); - - $request = new Request(); - $request->setMethod('POST'); - $request->query->set('_method', array('delete', 'patch')); - $this->assertSame('POST', $request->getMethod(), '->getMethod() returns the request method if invalid type is defined in query'); - } - - /** - * @dataProvider getClientIpsProvider - */ - public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies) - { - $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies); - - $this->assertEquals($expected[0], $request->getClientIp()); - } - - /** - * @dataProvider getClientIpsProvider - */ - public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $trustedProxies) - { - $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies); - - $this->assertEquals($expected, $request->getClientIps()); - } - - /** - * @dataProvider getClientIpsForwardedProvider - */ - public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies) - { - $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies); - - $this->assertEquals($expected, $request->getClientIps()); - } - - public function getClientIpsForwardedProvider() - { - // $expected $remoteAddr $httpForwarded $trustedProxies - return array( - array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null), - array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')), - array(array('88.88.88.88'), '127.0.0.1', 'for="88.88.88.88:80"', array('127.0.0.1')), - array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')), - array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3', array('::1')), - array(array('2001:db8:cafe::17'), '::1', 'for="[2001:db8:cafe::17]:4711', array('::1')), - ); - } - - public function getClientIpsProvider() - { - // $expected $remoteAddr $httpForwardedFor $trustedProxies - return array( - // simple IPv4 - array(array('88.88.88.88'), '88.88.88.88', null, null), - // trust the IPv4 remote addr - array(array('88.88.88.88'), '88.88.88.88', null, array('88.88.88.88')), - - // simple IPv6 - array(array('::1'), '::1', null, null), - // trust the IPv6 remote addr - array(array('::1'), '::1', null, array('::1')), - - // forwarded for with remote IPv4 addr not trusted - array(array('127.0.0.1'), '127.0.0.1', '88.88.88.88', null), - // forwarded for with remote IPv4 addr trusted + comma - array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88,', array('127.0.0.1')), - // forwarded for with remote IPv4 and all FF addrs trusted - array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')), - // forwarded for with remote IPv4 range trusted - array(array('88.88.88.88'), '123.45.67.89', '88.88.88.88', array('123.45.67.0/24')), - - // forwarded for with remote IPv6 addr not trusted - array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', null), - // forwarded for with remote IPv6 addr trusted - array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')), - // forwarded for with remote IPv6 range trusted - array(array('88.88.88.88'), '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88', array('2a01:198:603:0::/65')), - - // multiple forwarded for with remote IPv4 addr trusted - array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')), - // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted - array(array('87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')), - // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle - array(array('88.88.88.88', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21')), - // multiple forwarded for with remote IPv4 addr and all reverse proxies trusted - array(array('127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21', '88.88.88.88', '127.0.0.1')), - - // multiple forwarded for with remote IPv6 addr trusted - array(array('2620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')), - // multiple forwarded for with remote IPv6 addr and some reverse proxies trusted - array(array('3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3')), - // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle - array(array('2620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3,3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3')), - - // client IP with port - array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88:12345, 127.0.0.1', array('127.0.0.1')), - - // invalid forwarded IP is ignored - array(array('88.88.88.88'), '127.0.0.1', 'unknown,88.88.88.88', array('127.0.0.1')), - array(array('88.88.88.88'), '127.0.0.1', '}__test|O:21:"JDatabaseDriverMysqli":3:{s:2,88.88.88.88', array('127.0.0.1')), - ); - } - - /** - * @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException - * @dataProvider getClientIpsWithConflictingHeadersProvider - */ - public function testGetClientIpsWithConflictingHeaders($httpForwarded, $httpXForwardedFor) - { - $request = new Request(); - - $server = array( - 'REMOTE_ADDR' => '88.88.88.88', - 'HTTP_FORWARDED' => $httpForwarded, - 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, - ); - - Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_ALL | Request::HEADER_FORWARDED); - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $request->getClientIps(); - } - - /** - * @dataProvider getClientIpsWithConflictingHeadersProvider - */ - public function testGetClientIpsOnlyXHttpForwardedForTrusted($httpForwarded, $httpXForwardedFor) - { - $request = new Request(); - - $server = array( - 'REMOTE_ADDR' => '88.88.88.88', - 'HTTP_FORWARDED' => $httpForwarded, - 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, - ); - - Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_FOR); - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertSame(array_reverse(explode(',', $httpXForwardedFor)), $request->getClientIps()); - } - - public function getClientIpsWithConflictingHeadersProvider() - { - // $httpForwarded $httpXForwardedFor - return array( - array('for=87.65.43.21', '192.0.2.60'), - array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60'), - array('for=192.0.2.60', '192.0.2.60,87.65.43.21'), - array('for="::face", for=192.0.2.60', '192.0.2.60,192.0.2.43'), - array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60,87.65.43.21'), - ); - } - - /** - * @dataProvider getClientIpsWithAgreeingHeadersProvider - */ - public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwardedFor, $expectedIps) - { - $request = new Request(); - - $server = array( - 'REMOTE_ADDR' => '88.88.88.88', - 'HTTP_FORWARDED' => $httpForwarded, - 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, - ); - - Request::setTrustedProxies(array('88.88.88.88'), -1); - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $clientIps = $request->getClientIps(); - - $this->assertSame($expectedIps, $clientIps); - } - - public function getClientIpsWithAgreeingHeadersProvider() - { - // $httpForwarded $httpXForwardedFor - return array( - array('for="192.0.2.60"', '192.0.2.60', array('192.0.2.60')), - array('for=192.0.2.60, for=87.65.43.21', '192.0.2.60,87.65.43.21', array('87.65.43.21', '192.0.2.60')), - array('for="[::face]", for=192.0.2.60', '::face,192.0.2.60', array('192.0.2.60', '::face')), - array('for="192.0.2.60:80"', '192.0.2.60', array('192.0.2.60')), - array('for=192.0.2.60;proto=http;by=203.0.113.43', '192.0.2.60', array('192.0.2.60')), - array('for="[2001:db8:cafe::17]:4711"', '2001:db8:cafe::17', array('2001:db8:cafe::17')), - ); - } - - public function testGetContentWorksTwiceInDefaultMode() - { - $req = new Request(); - $this->assertEquals('', $req->getContent()); - $this->assertEquals('', $req->getContent()); - } - - public function testGetContentReturnsResource() - { - $req = new Request(); - $retval = $req->getContent(true); - $this->assertInternalType('resource', $retval); - $this->assertEquals('', fread($retval, 1)); - $this->assertTrue(feof($retval)); - } - - public function testGetContentReturnsResourceWhenContentSetInConstructor() - { - $req = new Request(array(), array(), array(), array(), array(), array(), 'MyContent'); - $resource = $req->getContent(true); - - $this->assertInternalType('resource', $resource); - $this->assertEquals('MyContent', stream_get_contents($resource)); - } - - public function testContentAsResource() - { - $resource = fopen('php://memory', 'r+'); - fwrite($resource, 'My other content'); - rewind($resource); - - $req = new Request(array(), array(), array(), array(), array(), array(), $resource); - $this->assertEquals('My other content', stream_get_contents($req->getContent(true))); - $this->assertEquals('My other content', $req->getContent()); - } - - /** - * @expectedException \LogicException - * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider - */ - public function testGetContentCantBeCalledTwiceWithResources($first, $second) - { - if (\PHP_VERSION_ID >= 50600) { - $this->markTestSkipped('PHP >= 5.6 allows to open php://input several times.'); - } - - $req = new Request(); - $req->getContent($first); - $req->getContent($second); - } - - public function getContentCantBeCalledTwiceWithResourcesProvider() - { - return array( - 'Resource then fetch' => array(true, false), - 'Resource then resource' => array(true, true), - ); - } - - /** - * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider - * @requires PHP 5.6 - */ - public function testGetContentCanBeCalledTwiceWithResources($first, $second) - { - $req = new Request(); - $a = $req->getContent($first); - $b = $req->getContent($second); - - if ($first) { - $a = stream_get_contents($a); - } - - if ($second) { - $b = stream_get_contents($b); - } - - $this->assertSame($a, $b); - } - - public function getContentCanBeCalledTwiceWithResourcesProvider() - { - return array( - 'Fetch then fetch' => array(false, false), - 'Fetch then resource' => array(false, true), - 'Resource then fetch' => array(true, false), - 'Resource then resource' => array(true, true), - ); - } - - public function provideOverloadedMethods() - { - return array( - array('PUT'), - array('DELETE'), - array('PATCH'), - array('put'), - array('delete'), - array('patch'), - ); - } - - /** - * @dataProvider provideOverloadedMethods - */ - public function testCreateFromGlobals($method) - { - $normalizedMethod = strtoupper($method); - - $_GET['foo1'] = 'bar1'; - $_POST['foo2'] = 'bar2'; - $_COOKIE['foo3'] = 'bar3'; - $_FILES['foo4'] = array('bar4'); - $_SERVER['foo5'] = 'bar5'; - - $request = Request::createFromGlobals(); - $this->assertEquals('bar1', $request->query->get('foo1'), '::fromGlobals() uses values from $_GET'); - $this->assertEquals('bar2', $request->request->get('foo2'), '::fromGlobals() uses values from $_POST'); - $this->assertEquals('bar3', $request->cookies->get('foo3'), '::fromGlobals() uses values from $_COOKIE'); - $this->assertEquals(array('bar4'), $request->files->get('foo4'), '::fromGlobals() uses values from $_FILES'); - $this->assertEquals('bar5', $request->server->get('foo5'), '::fromGlobals() uses values from $_SERVER'); - - unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']); - - $_SERVER['REQUEST_METHOD'] = $method; - $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - $request = RequestContentProxy::createFromGlobals(); - $this->assertEquals($normalizedMethod, $request->getMethod()); - $this->assertEquals('mycontent', $request->request->get('content')); - - unset($_SERVER['REQUEST_METHOD'], $_SERVER['CONTENT_TYPE']); - - Request::createFromGlobals(); - Request::enableHttpMethodParameterOverride(); - $_POST['_method'] = $method; - $_POST['foo6'] = 'bar6'; - $_SERVER['REQUEST_METHOD'] = 'PoSt'; - $request = Request::createFromGlobals(); - $this->assertEquals($normalizedMethod, $request->getMethod()); - $this->assertEquals('POST', $request->getRealMethod()); - $this->assertEquals('bar6', $request->request->get('foo6')); - - unset($_POST['_method'], $_POST['foo6'], $_SERVER['REQUEST_METHOD']); - $this->disableHttpMethodParameterOverride(); - } - - public function testOverrideGlobals() - { - $request = new Request(); - $request->initialize(array('foo' => 'bar')); - - // as the Request::overrideGlobals really work, it erase $_SERVER, so we must backup it - $server = $_SERVER; - - $request->overrideGlobals(); - - $this->assertEquals(array('foo' => 'bar'), $_GET); - - $request->initialize(array(), array('foo' => 'bar')); - $request->overrideGlobals(); - - $this->assertEquals(array('foo' => 'bar'), $_POST); - - $this->assertArrayNotHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); - - $request->headers->set('X_FORWARDED_PROTO', 'https'); - - Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL); - $this->assertFalse($request->isSecure()); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $this->assertTrue($request->isSecure()); - - $request->overrideGlobals(); - - $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); - - $request->headers->set('CONTENT_TYPE', 'multipart/form-data'); - $request->headers->set('CONTENT_LENGTH', 12345); - - $request->overrideGlobals(); - - $this->assertArrayHasKey('CONTENT_TYPE', $_SERVER); - $this->assertArrayHasKey('CONTENT_LENGTH', $_SERVER); - - $request->initialize(array('foo' => 'bar', 'baz' => 'foo')); - $request->query->remove('baz'); - - $request->overrideGlobals(); - - $this->assertEquals(array('foo' => 'bar'), $_GET); - $this->assertEquals('foo=bar', $_SERVER['QUERY_STRING']); - $this->assertEquals('foo=bar', $request->server->get('QUERY_STRING')); - - // restore initial $_SERVER array - $_SERVER = $server; - } - - public function testGetScriptName() - { - $request = new Request(); - $this->assertEquals('', $request->getScriptName()); - - $server = array(); - $server['SCRIPT_NAME'] = '/index.php'; - - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/index.php', $request->getScriptName()); - - $server = array(); - $server['ORIG_SCRIPT_NAME'] = '/frontend.php'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/frontend.php', $request->getScriptName()); - - $server = array(); - $server['SCRIPT_NAME'] = '/index.php'; - $server['ORIG_SCRIPT_NAME'] = '/frontend.php'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/index.php', $request->getScriptName()); - } - - public function testGetBasePath() - { - $request = new Request(); - $this->assertEquals('', $request->getBasePath()); - - $server = array(); - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('', $request->getBasePath()); - - $server = array(); - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - $server['SCRIPT_NAME'] = '/index.php'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('', $request->getBasePath()); - - $server = array(); - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - $server['PHP_SELF'] = '/index.php'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('', $request->getBasePath()); - - $server = array(); - $server['SCRIPT_FILENAME'] = '/some/where/index.php'; - $server['ORIG_SCRIPT_NAME'] = '/index.php'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('', $request->getBasePath()); - } - - public function testGetPathInfo() - { - $request = new Request(); - $this->assertEquals('/', $request->getPathInfo()); - - $server = array(); - $server['REQUEST_URI'] = '/path/info'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/path/info', $request->getPathInfo()); - - $server = array(); - $server['REQUEST_URI'] = '/path%20test/info'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/path%20test/info', $request->getPathInfo()); - - $server = array(); - $server['REQUEST_URI'] = '?a=b'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/', $request->getPathInfo()); - } - - public function testGetParameterPrecedence() - { - $request = new Request(); - $request->attributes->set('foo', 'attr'); - $request->query->set('foo', 'query'); - $request->request->set('foo', 'body'); - - $this->assertSame('attr', $request->get('foo')); - - $request->attributes->remove('foo'); - $this->assertSame('query', $request->get('foo')); - - $request->query->remove('foo'); - $this->assertSame('body', $request->get('foo')); - - $request->request->remove('foo'); - $this->assertNull($request->get('foo')); - } - - public function testGetPreferredLanguage() - { - $request = new Request(); - $this->assertNull($request->getPreferredLanguage()); - $this->assertNull($request->getPreferredLanguage(array())); - $this->assertEquals('fr', $request->getPreferredLanguage(array('fr'))); - $this->assertEquals('fr', $request->getPreferredLanguage(array('fr', 'en'))); - $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'fr'))); - $this->assertEquals('fr-ch', $request->getPreferredLanguage(array('fr-ch', 'fr-fr'))); - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); - $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'en-us'))); - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); - $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en-us; q=0.8'); - $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5'); - $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); - } - - public function testIsXmlHttpRequest() - { - $request = new Request(); - $this->assertFalse($request->isXmlHttpRequest()); - - $request->headers->set('X-Requested-With', 'XMLHttpRequest'); - $this->assertTrue($request->isXmlHttpRequest()); - - $request->headers->remove('X-Requested-With'); - $this->assertFalse($request->isXmlHttpRequest()); - } - - /** - * @requires extension intl - */ - public function testIntlLocale() - { - $request = new Request(); - - $request->setDefaultLocale('fr'); - $this->assertEquals('fr', $request->getLocale()); - $this->assertEquals('fr', \Locale::getDefault()); - - $request->setLocale('en'); - $this->assertEquals('en', $request->getLocale()); - $this->assertEquals('en', \Locale::getDefault()); - - $request->setDefaultLocale('de'); - $this->assertEquals('en', $request->getLocale()); - $this->assertEquals('en', \Locale::getDefault()); - } - - public function testGetCharsets() - { - $request = new Request(); - $this->assertEquals(array(), $request->getCharsets()); - $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6'); - $this->assertEquals(array(), $request->getCharsets()); // testing caching - - $request = new Request(); - $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6'); - $this->assertEquals(array('ISO-8859-1', 'US-ASCII', 'UTF-8', 'ISO-10646-UCS-2'), $request->getCharsets()); - - $request = new Request(); - $request->headers->set('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'); - $this->assertEquals(array('ISO-8859-1', 'utf-8', '*'), $request->getCharsets()); - } - - public function testGetEncodings() - { - $request = new Request(); - $this->assertEquals(array(), $request->getEncodings()); - $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch'); - $this->assertEquals(array(), $request->getEncodings()); // testing caching - - $request = new Request(); - $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch'); - $this->assertEquals(array('gzip', 'deflate', 'sdch'), $request->getEncodings()); - - $request = new Request(); - $request->headers->set('Accept-Encoding', 'gzip;q=0.4,deflate;q=0.9,compress;q=0.7'); - $this->assertEquals(array('deflate', 'compress', 'gzip'), $request->getEncodings()); - } - - public function testGetAcceptableContentTypes() - { - $request = new Request(); - $this->assertEquals(array(), $request->getAcceptableContentTypes()); - $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*'); - $this->assertEquals(array(), $request->getAcceptableContentTypes()); // testing caching - - $request = new Request(); - $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*'); - $this->assertEquals(array('application/vnd.wap.wmlscriptc', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml', 'application/xhtml+xml', 'text/html', 'multipart/mixed', '*/*'), $request->getAcceptableContentTypes()); - } - - public function testGetLanguages() - { - $request = new Request(); - $this->assertEquals(array(), $request->getLanguages()); - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); - $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages()); - $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages()); - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en-us; q=0.6, en; q=0.8'); - $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test out of order qvalues - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, en, en-us'); - $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test equal weighting without qvalues - - $request = new Request(); - $request->headers->set('Accept-language', 'zh; q=0.6, en, en-us; q=0.6'); - $this->assertEquals(array('en', 'zh', 'en_US'), $request->getLanguages()); // Test equal weighting with qvalues - - $request = new Request(); - $request->headers->set('Accept-language', 'zh, i-cherokee; q=0.6'); - $this->assertEquals(array('zh', 'cherokee'), $request->getLanguages()); - } - - public function testGetRequestFormat() - { - $request = new Request(); - $this->assertEquals('html', $request->getRequestFormat()); - - // Ensure that setting different default values over time is possible, - // aka. setRequestFormat determines the state. - $this->assertEquals('json', $request->getRequestFormat('json')); - $this->assertEquals('html', $request->getRequestFormat('html')); - - $request = new Request(); - $this->assertNull($request->getRequestFormat(null)); - - $request = new Request(); - $this->assertNull($request->setRequestFormat('foo')); - $this->assertEquals('foo', $request->getRequestFormat(null)); - - $request = new Request(array('_format' => 'foo')); - $this->assertEquals('html', $request->getRequestFormat()); - } - - public function testHasSession() - { - $request = new Request(); - - $this->assertFalse($request->hasSession()); - $request->setSession(new Session(new MockArraySessionStorage())); - $this->assertTrue($request->hasSession()); - } - - public function testGetSession() - { - $request = new Request(); - - $request->setSession(new Session(new MockArraySessionStorage())); - $this->assertTrue($request->hasSession()); - - $session = $request->getSession(); - $this->assertObjectHasAttribute('storage', $session); - $this->assertObjectHasAttribute('flashName', $session); - $this->assertObjectHasAttribute('attributeName', $session); - } - - public function testHasPreviousSession() - { - $request = new Request(); - - $this->assertFalse($request->hasPreviousSession()); - $request->cookies->set('MOCKSESSID', 'foo'); - $this->assertFalse($request->hasPreviousSession()); - $request->setSession(new Session(new MockArraySessionStorage())); - $this->assertTrue($request->hasPreviousSession()); - } - - public function testToString() - { - $request = new Request(); - - $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); - $request->cookies->set('Foo', 'Bar'); - - $asString = (string) $request; - - $this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $asString); - $this->assertContains('Cookie: Foo=Bar', $asString); - - $request->cookies->set('Another', 'Cookie'); - - $asString = (string) $request; - - $this->assertContains('Cookie: Foo=Bar; Another=Cookie', $asString); - } - - public function testIsMethod() - { - $request = new Request(); - $request->setMethod('POST'); - $this->assertTrue($request->isMethod('POST')); - $this->assertTrue($request->isMethod('post')); - $this->assertFalse($request->isMethod('GET')); - $this->assertFalse($request->isMethod('get')); - - $request->setMethod('GET'); - $this->assertTrue($request->isMethod('GET')); - $this->assertTrue($request->isMethod('get')); - $this->assertFalse($request->isMethod('POST')); - $this->assertFalse($request->isMethod('post')); - } - - /** - * @dataProvider getBaseUrlData - */ - public function testGetBaseUrl($uri, $server, $expectedBaseUrl, $expectedPathInfo) - { - $request = Request::create($uri, 'GET', array(), array(), array(), $server); - - $this->assertSame($expectedBaseUrl, $request->getBaseUrl(), 'baseUrl'); - $this->assertSame($expectedPathInfo, $request->getPathInfo(), 'pathInfo'); - } - - public function getBaseUrlData() - { - return array( - array( - '/fruit/strawberry/1234index.php/blah', - array( - 'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/fruit/index.php', - 'SCRIPT_NAME' => '/fruit/index.php', - 'PHP_SELF' => '/fruit/index.php', - ), - '/fruit', - '/strawberry/1234index.php/blah', - ), - array( - '/fruit/strawberry/1234index.php/blah', - array( - 'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/index.php', - 'SCRIPT_NAME' => '/index.php', - 'PHP_SELF' => '/index.php', - ), - '', - '/fruit/strawberry/1234index.php/blah', - ), - array( - '/foo%20bar/', - array( - 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', - 'SCRIPT_NAME' => '/foo bar/app.php', - 'PHP_SELF' => '/foo bar/app.php', - ), - '/foo%20bar', - '/', - ), - array( - '/foo%20bar/home', - array( - 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', - 'SCRIPT_NAME' => '/foo bar/app.php', - 'PHP_SELF' => '/foo bar/app.php', - ), - '/foo%20bar', - '/home', - ), - array( - '/foo%20bar/app.php/home', - array( - 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', - 'SCRIPT_NAME' => '/foo bar/app.php', - 'PHP_SELF' => '/foo bar/app.php', - ), - '/foo%20bar/app.php', - '/home', - ), - array( - '/foo%20bar/app.php/home%3Dbaz', - array( - 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', - 'SCRIPT_NAME' => '/foo bar/app.php', - 'PHP_SELF' => '/foo bar/app.php', - ), - '/foo%20bar/app.php', - '/home%3Dbaz', - ), - array( - '/foo/bar+baz', - array( - 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', - 'SCRIPT_NAME' => '/foo/app.php', - 'PHP_SELF' => '/foo/app.php', - ), - '/foo', - '/bar+baz', - ), - ); - } - - /** - * @dataProvider urlencodedStringPrefixData - */ - public function testUrlencodedStringPrefix($string, $prefix, $expect) - { - $request = new Request(); - - $me = new \ReflectionMethod($request, 'getUrlencodedPrefix'); - $me->setAccessible(true); - - $this->assertSame($expect, $me->invoke($request, $string, $prefix)); - } - - public function urlencodedStringPrefixData() - { - return array( - array('foo', 'foo', 'foo'), - array('fo%6f', 'foo', 'fo%6f'), - array('foo/bar', 'foo', 'foo'), - array('fo%6f/bar', 'foo', 'fo%6f'), - array('f%6f%6f/bar', 'foo', 'f%6f%6f'), - array('%66%6F%6F/bar', 'foo', '%66%6F%6F'), - array('fo+o/bar', 'fo+o', 'fo+o'), - array('fo%2Bo/bar', 'fo+o', 'fo%2Bo'), - ); - } - - private function disableHttpMethodParameterOverride() - { - $class = new \ReflectionClass('Symfony\\Component\\HttpFoundation\\Request'); - $property = $class->getProperty('httpMethodParameterOverride'); - $property->setAccessible(true); - $property->setValue(false); - } - - private function getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies) - { - $request = new Request(); - - $server = array('REMOTE_ADDR' => $remoteAddr); - if (null !== $httpForwardedFor) { - $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor; - } - - if ($trustedProxies) { - Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL); - } - - $request->initialize(array(), array(), array(), array(), array(), $server); - - return $request; - } - - private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies) - { - $request = new Request(); - - $server = array('REMOTE_ADDR' => $remoteAddr); - - if (null !== $httpForwarded) { - $server['HTTP_FORWARDED'] = $httpForwarded; - } - - if ($trustedProxies) { - Request::setTrustedProxies($trustedProxies, Request::HEADER_FORWARDED); - } - - $request->initialize(array(), array(), array(), array(), array(), $server); - - return $request; - } - - public function testTrustedProxiesXForwardedFor() - { - $request = Request::create('http://example.com/'); - $request->server->set('REMOTE_ADDR', '3.3.3.3'); - $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2'); - $request->headers->set('X_FORWARDED_HOST', 'foo.example.com:1234, real.example.com:8080'); - $request->headers->set('X_FORWARDED_PROTO', 'https'); - $request->headers->set('X_FORWARDED_PORT', 443); - - // no trusted proxies - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // disabling proxy trusting - Request::setTrustedProxies(array(), Request::HEADER_X_FORWARDED_ALL); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // request is forwarded by a non-trusted proxy - Request::setTrustedProxies(array('2.2.2.2'), Request::HEADER_X_FORWARDED_ALL); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // trusted proxy via setTrustedProxies() - Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL); - $this->assertEquals('1.1.1.1', $request->getClientIp()); - $this->assertEquals('foo.example.com', $request->getHost()); - $this->assertEquals(443, $request->getPort()); - $this->assertTrue($request->isSecure()); - - // trusted proxy via setTrustedProxies() - Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // check various X_FORWARDED_PROTO header values - Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL); - $request->headers->set('X_FORWARDED_PROTO', 'ssl'); - $this->assertTrue($request->isSecure()); - - $request->headers->set('X_FORWARDED_PROTO', 'https, http'); - $this->assertTrue($request->isSecure()); - } - - /** - * @group legacy - * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead. - */ - public function testLegacyTrustedProxies() - { - $request = Request::create('http://example.com/'); - $request->server->set('REMOTE_ADDR', '3.3.3.3'); - $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2'); - $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080'); - $request->headers->set('X_FORWARDED_PROTO', 'https'); - $request->headers->set('X_FORWARDED_PORT', 443); - $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4'); - $request->headers->set('X_MY_HOST', 'my.example.com'); - $request->headers->set('X_MY_PROTO', 'http'); - $request->headers->set('X_MY_PORT', 81); - - Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL); - - // custom header names - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_MY_FOR'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_MY_HOST'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_MY_PORT'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_MY_PROTO'); - $this->assertEquals('4.4.4.4', $request->getClientIp()); - $this->assertEquals('my.example.com', $request->getHost()); - $this->assertEquals(81, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // disabling via empty header names - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, null); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, null); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, null); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, null); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - //reset - Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO'); - } - - public function testTrustedProxiesForwarded() - { - $request = Request::create('http://example.com/'); - $request->server->set('REMOTE_ADDR', '3.3.3.3'); - $request->headers->set('FORWARDED', 'for=1.1.1.1, host=foo.example.com:8080, proto=https, for=2.2.2.2, host=real.example.com:8080'); - - // no trusted proxies - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // disabling proxy trusting - Request::setTrustedProxies(array(), Request::HEADER_FORWARDED); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // request is forwarded by a non-trusted proxy - Request::setTrustedProxies(array('2.2.2.2'), Request::HEADER_FORWARDED); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // trusted proxy via setTrustedProxies() - Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_FORWARDED); - $this->assertEquals('1.1.1.1', $request->getClientIp()); - $this->assertEquals('foo.example.com', $request->getHost()); - $this->assertEquals(8080, $request->getPort()); - $this->assertTrue($request->isSecure()); - - // trusted proxy via setTrustedProxies() - Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2'), Request::HEADER_FORWARDED); - $this->assertEquals('3.3.3.3', $request->getClientIp()); - $this->assertEquals('example.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - $this->assertFalse($request->isSecure()); - - // check various X_FORWARDED_PROTO header values - Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_FORWARDED); - $request->headers->set('FORWARDED', 'proto=ssl'); - $this->assertTrue($request->isSecure()); - - $request->headers->set('FORWARDED', 'proto=https, proto=http'); - $this->assertTrue($request->isSecure()); - } - - /** - * @group legacy - * @expectedException \InvalidArgumentException - */ - public function testSetTrustedProxiesInvalidHeaderName() - { - Request::create('http://example.com/'); - Request::setTrustedHeaderName('bogus name', 'X_MY_FOR'); - } - - /** - * @group legacy - * @expectedException \InvalidArgumentException - */ - public function testGetTrustedProxiesInvalidHeaderName() - { - Request::create('http://example.com/'); - Request::getTrustedHeaderName('bogus name'); - } - - /** - * @dataProvider iisRequestUriProvider - */ - public function testIISRequestUri($headers, $server, $expectedRequestUri) - { - $request = new Request(); - $request->headers->replace($headers); - $request->server->replace($server); - - $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct'); - - $subRequestUri = '/bar/foo'; - $subRequest = Request::create($subRequestUri, 'get', array(), array(), array(), $request->server->all()); - $this->assertEquals($subRequestUri, $subRequest->getRequestUri(), '->getRequestUri() is correct in sub request'); - } - - public function iisRequestUriProvider() - { - return array( - array( - array(), - array( - 'IIS_WasUrlRewritten' => '1', - 'UNENCODED_URL' => '/foo/bar', - ), - '/foo/bar', - ), - array( - array(), - array( - 'ORIG_PATH_INFO' => '/foo/bar', - ), - '/foo/bar', - ), - array( - array(), - array( - 'ORIG_PATH_INFO' => '/foo/bar', - 'QUERY_STRING' => 'foo=bar', - ), - '/foo/bar?foo=bar', - ), - ); - } - - public function testTrustedHosts() - { - // create a request - $request = Request::create('/'); - - // no trusted host set -> no host check - $request->headers->set('host', 'evil.com'); - $this->assertEquals('evil.com', $request->getHost()); - - // add a trusted domain and all its subdomains - Request::setTrustedHosts(array('^([a-z]{9}\.)?trusted\.com$')); - - // untrusted host - $request->headers->set('host', 'evil.com'); - try { - $request->getHost(); - $this->fail('Request::getHost() should throw an exception when host is not trusted.'); - } catch (SuspiciousOperationException $e) { - $this->assertEquals('Untrusted Host "evil.com".', $e->getMessage()); - } - - // trusted hosts - $request->headers->set('host', 'trusted.com'); - $this->assertEquals('trusted.com', $request->getHost()); - $this->assertEquals(80, $request->getPort()); - - $request->server->set('HTTPS', true); - $request->headers->set('host', 'trusted.com'); - $this->assertEquals('trusted.com', $request->getHost()); - $this->assertEquals(443, $request->getPort()); - $request->server->set('HTTPS', false); - - $request->headers->set('host', 'trusted.com:8000'); - $this->assertEquals('trusted.com', $request->getHost()); - $this->assertEquals(8000, $request->getPort()); - - $request->headers->set('host', 'subdomain.trusted.com'); - $this->assertEquals('subdomain.trusted.com', $request->getHost()); - } - - public function testSetTrustedHostsDoesNotBreakOnSpecialCharacters() - { - Request::setTrustedHosts(array('localhost(\.local){0,1}#,example.com', 'localhost')); - - $request = Request::create('/'); - $request->headers->set('host', 'localhost'); - $this->assertSame('localhost', $request->getHost()); - } - - public function testFactory() - { - Request::setFactory(function (array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { - return new NewRequest(); - }); - - $this->assertEquals('foo', Request::create('/')->getFoo()); - - Request::setFactory(null); - } - - /** - * @dataProvider getLongHostNames - */ - public function testVeryLongHosts($host) - { - $start = microtime(true); - - $request = Request::create('/'); - $request->headers->set('host', $host); - $this->assertEquals($host, $request->getHost()); - $this->assertLessThan(5, microtime(true) - $start); - } - - /** - * @dataProvider getHostValidities - */ - public function testHostValidity($host, $isValid, $expectedHost = null, $expectedPort = null) - { - $request = Request::create('/'); - $request->headers->set('host', $host); - - if ($isValid) { - $this->assertSame($expectedHost ?: $host, $request->getHost()); - if ($expectedPort) { - $this->assertSame($expectedPort, $request->getPort()); - } - } else { - if (method_exists($this, 'expectException')) { - $this->expectException(SuspiciousOperationException::class); - $this->expectExceptionMessage('Invalid Host'); - } else { - $this->setExpectedException(SuspiciousOperationException::class, 'Invalid Host'); - } - - $request->getHost(); - } - } - - public function getHostValidities() - { - return array( - array('.a', false), - array('a..', false), - array('a.', true), - array("\xE9", false), - array('[::1]', true), - array('[::1]:80', true, '[::1]', 80), - array(str_repeat('.', 101), false), - ); - } - - public function getLongHostNames() - { - return array( - array('a'.str_repeat('.a', 40000)), - array(str_repeat(':', 101)), - ); - } - - /** - * @dataProvider methodIdempotentProvider - */ - public function testMethodIdempotent($method, $idempotent) - { - $request = new Request(); - $request->setMethod($method); - $this->assertEquals($idempotent, $request->isMethodIdempotent()); - } - - public function methodIdempotentProvider() - { - return array( - array('HEAD', true), - array('GET', true), - array('POST', false), - array('PUT', true), - array('PATCH', false), - array('DELETE', true), - array('PURGE', true), - array('OPTIONS', true), - array('TRACE', true), - array('CONNECT', false), - ); - } - - /** - * @dataProvider methodSafeProvider - */ - public function testMethodSafe($method, $safe) - { - $request = new Request(); - $request->setMethod($method); - $this->assertEquals($safe, $request->isMethodSafe(false)); - } - - public function methodSafeProvider() - { - return array( - array('HEAD', true), - array('GET', true), - array('POST', false), - array('PUT', false), - array('PATCH', false), - array('DELETE', false), - array('PURGE', false), - array('OPTIONS', true), - array('TRACE', true), - array('CONNECT', false), - ); - } - - /** - * @group legacy - * @expectedDeprecation Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead. - */ - public function testMethodSafeChecksCacheable() - { - $request = new Request(); - $request->setMethod('OPTIONS'); - $this->assertFalse($request->isMethodSafe()); - } - - /** - * @dataProvider methodCacheableProvider - */ - public function testMethodCacheable($method, $cacheable) - { - $request = new Request(); - $request->setMethod($method); - $this->assertEquals($cacheable, $request->isMethodCacheable()); - } - - public function methodCacheableProvider() - { - return array( - array('HEAD', true), - array('GET', true), - array('POST', false), - array('PUT', false), - array('PATCH', false), - array('DELETE', false), - array('PURGE', false), - array('OPTIONS', false), - array('TRACE', false), - array('CONNECT', false), - ); - } - - /** - * @group legacy - */ - public function testGetTrustedHeaderName() - { - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL); - - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertSame('X_FORWARDED_FOR', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertSame('X_FORWARDED_HOST', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertSame('X_FORWARDED_PORT', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertSame('X_FORWARDED_PROTO', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED); - - $this->assertSame('FORWARDED', Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'A'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'B'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'C'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'D'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'E'); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED); - - $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL); - - $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - $this->assertSame('B', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)); - $this->assertSame('C', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST)); - $this->assertSame('D', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT)); - $this->assertSame('E', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO)); - - Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED); - - $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED)); - - //reset - Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT'); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO'); - } - - /** - * @dataProvider protocolVersionProvider - */ - public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expected) - { - if ($trustedProxy) { - Request::setTrustedProxies(array('1.1.1.1'), -1); - } - - $request = new Request(); - $request->server->set('SERVER_PROTOCOL', $serverProtocol); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('Via', $via); - - $this->assertSame($expected, $request->getProtocolVersion()); - } - - public function protocolVersionProvider() - { - return array( - 'untrusted without via' => array('HTTP/2.0', false, '', 'HTTP/2.0'), - 'untrusted with via' => array('HTTP/2.0', false, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/2.0'), - 'trusted without via' => array('HTTP/2.0', true, '', 'HTTP/2.0'), - 'trusted with via' => array('HTTP/2.0', true, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'), - 'trusted with via and protocol name' => array('HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'), - 'trusted with broken via' => array('HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'), - 'trusted with partially-broken via' => array('HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'), - ); - } - - public function nonstandardRequestsData() - { - return array( - array('', '', '/', 'http://host:8080/', ''), - array('/', '', '/', 'http://host:8080/', ''), - - array('hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), - array('/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), - - array('', 'a=b', '/', 'http://host:8080/?a=b'), - array('?a=b', 'a=b', '/', 'http://host:8080/?a=b'), - array('/?a=b', 'a=b', '/', 'http://host:8080/?a=b'), - - array('x', 'a=b', '/x', 'http://host:8080/x?a=b'), - array('x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), - array('/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), - - array('hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), - array('/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), - - array('hello/app.php/x', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), - array('hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), - array('/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), - ); - } - - /** - * @dataProvider nonstandardRequestsData - */ - public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null) - { - if (null === $expectedBaseUrl) { - $expectedBaseUrl = $expectedBasePath; - } - - $server = array( - 'HTTP_HOST' => 'host:8080', - 'SERVER_PORT' => '8080', - 'QUERY_STRING' => $queryString, - 'PHP_SELF' => '/hello/app.php', - 'SCRIPT_FILENAME' => '/some/path/app.php', - 'REQUEST_URI' => $requestUri, - ); - - $request = new Request(array(), array(), array(), array(), array(), $server); - - $this->assertEquals($expectedPathInfo, $request->getPathInfo()); - $this->assertEquals($expectedUri, $request->getUri()); - $this->assertEquals($queryString, $request->getQueryString()); - $this->assertEquals(8080, $request->getPort()); - $this->assertEquals('host:8080', $request->getHttpHost()); - $this->assertEquals($expectedBaseUrl, $request->getBaseUrl()); - $this->assertEquals($expectedBasePath, $request->getBasePath()); - } - - public function testTrustedHost() - { - Request::setTrustedProxies(array('1.1.1.1'), -1); - - $request = Request::create('/'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('Forwarded', 'host=localhost:8080'); - $request->headers->set('X-Forwarded-Host', 'localhost:8080'); - - $this->assertSame('localhost:8080', $request->getHttpHost()); - $this->assertSame(8080, $request->getPort()); - - $request = Request::create('/'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('Forwarded', 'host="[::1]:443"'); - $request->headers->set('X-Forwarded-Host', '[::1]:443'); - $request->headers->set('X-Forwarded-Port', 443); - - $this->assertSame('[::1]:443', $request->getHttpHost()); - $this->assertSame(443, $request->getPort()); - } - - public function testTrustedPort() - { - Request::setTrustedProxies(array('1.1.1.1'), -1); - - $request = Request::create('/'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('Forwarded', 'host=localhost:8080'); - $request->headers->set('X-Forwarded-Port', 8080); - - $this->assertSame(8080, $request->getPort()); - - $request = Request::create('/'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('Forwarded', 'host=localhost'); - $request->headers->set('X-Forwarded-Port', 80); - - $this->assertSame(80, $request->getPort()); - - $request = Request::create('/'); - $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('Forwarded', 'host="[::1]"'); - $request->headers->set('X-Forwarded-Proto', 'https'); - $request->headers->set('X-Forwarded-Port', 443); - - $this->assertSame(443, $request->getPort()); - } -} - -class RequestContentProxy extends Request -{ - public function getContent($asResource = false) - { - return http_build_query(array('_method' => 'PUT', 'content' => 'mycontent'), '', '&'); - } -} - -class NewRequest extends Request -{ - public function getFoo() - { - return 'foo'; - } -} diff --git a/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php b/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php deleted file mode 100644 index 22f25e9..0000000 --- a/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; - -/** - * @requires PHP 7.0 - */ -class ResponseFunctionalTest extends TestCase -{ - private static $server; - - public static function setUpBeforeClass() - { - $spec = array( - 1 => array('file', '/dev/null', 'w'), - 2 => array('file', '/dev/null', 'w'), - ); - if (!self::$server = @proc_open('exec php -S localhost:8054', $spec, $pipes, __DIR__.'/Fixtures/response-functional')) { - self::markTestSkipped('PHP server unable to start.'); - } - sleep(1); - } - - public static function tearDownAfterClass() - { - if (self::$server) { - proc_terminate(self::$server); - proc_close(self::$server); - } - } - - /** - * @dataProvider provideCookie - */ - public function testCookie($fixture) - { - $result = file_get_contents(sprintf('http://localhost:8054/%s.php', $fixture)); - $this->assertStringMatchesFormatFile(__DIR__.sprintf('/Fixtures/response-functional/%s.expected', $fixture), $result); - } - - public function provideCookie() - { - foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) { - yield array(pathinfo($file, PATHINFO_FILENAME)); - } - } -} diff --git a/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php b/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php deleted file mode 100644 index 06e2d41..0000000 --- a/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php +++ /dev/null @@ -1,363 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Cookie; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; - -/** - * @group time-sensitive - */ -class ResponseHeaderBagTest extends TestCase -{ - public function testAllPreserveCase() - { - $headers = array( - 'fOo' => 'BAR', - 'ETag' => 'xyzzy', - 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', - 'P3P' => 'CP="CAO PSA OUR"', - 'WWW-Authenticate' => 'Basic realm="WallyWorld"', - 'X-UA-Compatible' => 'IE=edge,chrome=1', - 'X-XSS-Protection' => '1; mode=block', - ); - - $bag = new ResponseHeaderBag($headers); - $allPreservedCase = $bag->allPreserveCase(); - - foreach (array_keys($headers) as $headerName) { - $this->assertArrayHasKey($headerName, $allPreservedCase, '->allPreserveCase() gets all input keys in original case'); - } - } - - public function testCacheControlHeader() - { - $bag = new ResponseHeaderBag(array()); - $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('no-cache')); - - $bag = new ResponseHeaderBag(array('Cache-Control' => 'public')); - $this->assertEquals('public', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('public')); - - $bag = new ResponseHeaderBag(array('ETag' => 'abcde')); - $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('private')); - $this->assertTrue($bag->hasCacheControlDirective('must-revalidate')); - $this->assertFalse($bag->hasCacheControlDirective('max-age')); - - $bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT')); - $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array( - 'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT', - 'Cache-Control' => 'max-age=3600', - )); - $this->assertEquals('max-age=3600, private', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde')); - $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde')); - $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100')); - $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100')); - $this->assertEquals('s-maxage=100', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100')); - $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100')); - $this->assertEquals('max-age=100, public', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(); - $bag->set('Last-Modified', 'abcde'); - $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(); - $bag->set('Cache-Control', array('public', 'must-revalidate')); - $this->assertCount(1, $bag->get('Cache-Control', null, false)); - $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control')); - - $bag = new ResponseHeaderBag(); - $bag->set('Cache-Control', 'public'); - $bag->set('Cache-Control', 'must-revalidate', false); - $this->assertCount(1, $bag->get('Cache-Control', null, false)); - $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control')); - } - - public function testCacheControlClone() - { - $headers = array('foo' => 'bar'); - $bag1 = new ResponseHeaderBag($headers); - $bag2 = new ResponseHeaderBag($bag1->allPreserveCase()); - $this->assertEquals($bag1->allPreserveCase(), $bag2->allPreserveCase()); - } - - public function testToStringIncludesCookieHeaders() - { - $bag = new ResponseHeaderBag(array()); - $bag->setCookie(new Cookie('foo', 'bar')); - - $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag); - - $bag->clearCookie('foo'); - - $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag); - } - - public function testClearCookieSecureNotHttpOnly() - { - $bag = new ResponseHeaderBag(array()); - - $bag->clearCookie('foo', '/', null, true, false); - - $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag); - } - - public function testReplace() - { - $bag = new ResponseHeaderBag(array()); - $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('no-cache')); - - $bag->replace(array('Cache-Control' => 'public')); - $this->assertEquals('public', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('public')); - } - - public function testReplaceWithRemove() - { - $bag = new ResponseHeaderBag(array()); - $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('no-cache')); - - $bag->remove('Cache-Control'); - $bag->replace(array()); - $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); - $this->assertTrue($bag->hasCacheControlDirective('no-cache')); - } - - public function testCookiesWithSameNames() - { - $bag = new ResponseHeaderBag(); - $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); - $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar')); - $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo')); - $bag->setCookie(new Cookie('foo', 'bar')); - - $this->assertCount(4, $bag->getCookies()); - $this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie')); - $this->assertEquals(array( - 'foo=bar; path=/path/foo; domain=foo.bar; httponly', - 'foo=bar; path=/path/bar; domain=foo.bar; httponly', - 'foo=bar; path=/path/bar; domain=bar.foo; httponly', - 'foo=bar; path=/; httponly', - ), $bag->get('set-cookie', null, false)); - - $this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag); - $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag); - $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag); - $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag); - - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - - $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/foo']); - $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/bar']); - $this->assertArrayHasKey('foo', $cookies['bar.foo']['/path/bar']); - $this->assertArrayHasKey('foo', $cookies['']['/']); - } - - public function testRemoveCookie() - { - $bag = new ResponseHeaderBag(); - $this->assertFalse($bag->has('set-cookie')); - - $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); - $bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar')); - $this->assertTrue($bag->has('set-cookie')); - - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - $this->assertArrayHasKey('/path/foo', $cookies['foo.bar']); - - $bag->removeCookie('foo', '/path/foo', 'foo.bar'); - $this->assertTrue($bag->has('set-cookie')); - - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - $this->assertArrayNotHasKey('/path/foo', $cookies['foo.bar']); - - $bag->removeCookie('bar', '/path/bar', 'foo.bar'); - $this->assertFalse($bag->has('set-cookie')); - - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - $this->assertArrayNotHasKey('foo.bar', $cookies); - } - - public function testRemoveCookieWithNullRemove() - { - $bag = new ResponseHeaderBag(); - $bag->setCookie(new Cookie('foo', 'bar', 0)); - $bag->setCookie(new Cookie('bar', 'foo', 0)); - - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - $this->assertArrayHasKey('/', $cookies['']); - - $bag->removeCookie('foo', null); - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - $this->assertArrayNotHasKey('foo', $cookies['']['/']); - - $bag->removeCookie('bar', null); - $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); - $this->assertFalse(isset($cookies['']['/']['bar'])); - } - - public function testSetCookieHeader() - { - $bag = new ResponseHeaderBag(); - $bag->set('set-cookie', 'foo=bar'); - $this->assertEquals(array(new Cookie('foo', 'bar', 0, '/', null, false, false, true)), $bag->getCookies()); - - $bag->set('set-cookie', 'foo2=bar2', false); - $this->assertEquals(array( - new Cookie('foo', 'bar', 0, '/', null, false, false, true), - new Cookie('foo2', 'bar2', 0, '/', null, false, false, true), - ), $bag->getCookies()); - - $bag->remove('set-cookie'); - $this->assertEquals(array(), $bag->getCookies()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testGetCookiesWithInvalidArgument() - { - $bag = new ResponseHeaderBag(); - - $bag->getCookies('invalid_argument'); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testMakeDispositionInvalidDisposition() - { - $headers = new ResponseHeaderBag(); - - $headers->makeDisposition('invalid', 'foo.html'); - } - - /** - * @dataProvider provideMakeDisposition - */ - public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected) - { - $headers = new ResponseHeaderBag(); - - $this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback)); - } - - public function testToStringDoesntMessUpHeaders() - { - $headers = new ResponseHeaderBag(); - - $headers->set('Location', 'http://www.symfony.com'); - $headers->set('Content-type', 'text/html'); - - (string) $headers; - - $allHeaders = $headers->allPreserveCase(); - $this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']); - $this->assertEquals(array('text/html'), $allHeaders['Content-type']); - } - - public function provideMakeDisposition() - { - return array( - array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'), - array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'), - array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'), - array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'), - array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'), - array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'), - ); - } - - /** - * @dataProvider provideMakeDispositionFail - * @expectedException \InvalidArgumentException - */ - public function testMakeDispositionFail($disposition, $filename) - { - $headers = new ResponseHeaderBag(); - - $headers->makeDisposition($disposition, $filename); - } - - public function provideMakeDispositionFail() - { - return array( - array('attachment', 'foo%20bar.html'), - array('attachment', 'foo/bar.html'), - array('attachment', '/foo.html'), - array('attachment', 'foo\bar.html'), - array('attachment', '\foo.html'), - array('attachment', 'föö.html'), - ); - } - - public function testDateHeaderAddedOnCreation() - { - $now = time(); - - $bag = new ResponseHeaderBag(); - $this->assertTrue($bag->has('Date')); - - $this->assertEquals($now, $bag->getDate('Date')->getTimestamp()); - } - - public function testDateHeaderCanBeSetOnCreation() - { - $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT'; - $bag = new ResponseHeaderBag(array('Date' => $someDate)); - - $this->assertEquals($someDate, $bag->get('Date')); - } - - public function testDateHeaderWillBeRecreatedWhenRemoved() - { - $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT'; - $bag = new ResponseHeaderBag(array('Date' => $someDate)); - $bag->remove('Date'); - - // a (new) Date header is still present - $this->assertTrue($bag->has('Date')); - $this->assertNotEquals($someDate, $bag->get('Date')); - } - - public function testDateHeaderWillBeRecreatedWhenHeadersAreReplaced() - { - $bag = new ResponseHeaderBag(); - $bag->replace(array()); - - $this->assertTrue($bag->has('Date')); - } - - private function assertSetCookieHeader($expected, ResponseHeaderBag $actual) - { - $this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual)); - } -} diff --git a/vendor/symfony/http-foundation/Tests/ResponseTest.php b/vendor/symfony/http-foundation/Tests/ResponseTest.php deleted file mode 100644 index 43fa9b7..0000000 --- a/vendor/symfony/http-foundation/Tests/ResponseTest.php +++ /dev/null @@ -1,1013 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; - -/** - * @group time-sensitive - */ -class ResponseTest extends ResponseTestCase -{ - public function testCreate() - { - $response = Response::create('foo', 301, array('Foo' => 'bar')); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); - $this->assertEquals(301, $response->getStatusCode()); - $this->assertEquals('bar', $response->headers->get('foo')); - } - - public function testToString() - { - $response = new Response(); - $response = explode("\r\n", $response); - $this->assertEquals('HTTP/1.0 200 OK', $response[0]); - $this->assertEquals('Cache-Control: no-cache, private', $response[1]); - } - - public function testClone() - { - $response = new Response(); - $responseClone = clone $response; - $this->assertEquals($response, $responseClone); - } - - public function testSendHeaders() - { - $response = new Response(); - $headers = $response->sendHeaders(); - $this->assertObjectHasAttribute('headers', $headers); - $this->assertObjectHasAttribute('content', $headers); - $this->assertObjectHasAttribute('version', $headers); - $this->assertObjectHasAttribute('statusCode', $headers); - $this->assertObjectHasAttribute('statusText', $headers); - $this->assertObjectHasAttribute('charset', $headers); - } - - public function testSend() - { - $response = new Response(); - $responseSend = $response->send(); - $this->assertObjectHasAttribute('headers', $responseSend); - $this->assertObjectHasAttribute('content', $responseSend); - $this->assertObjectHasAttribute('version', $responseSend); - $this->assertObjectHasAttribute('statusCode', $responseSend); - $this->assertObjectHasAttribute('statusText', $responseSend); - $this->assertObjectHasAttribute('charset', $responseSend); - } - - public function testGetCharset() - { - $response = new Response(); - $charsetOrigin = 'UTF-8'; - $response->setCharset($charsetOrigin); - $charset = $response->getCharset(); - $this->assertEquals($charsetOrigin, $charset); - } - - public function testIsCacheable() - { - $response = new Response(); - $this->assertFalse($response->isCacheable()); - } - - public function testIsCacheableWithErrorCode() - { - $response = new Response('', 500); - $this->assertFalse($response->isCacheable()); - } - - public function testIsCacheableWithNoStoreDirective() - { - $response = new Response(); - $response->headers->set('cache-control', 'private'); - $this->assertFalse($response->isCacheable()); - } - - public function testIsCacheableWithSetTtl() - { - $response = new Response(); - $response->setTtl(10); - $this->assertTrue($response->isCacheable()); - } - - public function testMustRevalidate() - { - $response = new Response(); - $this->assertFalse($response->mustRevalidate()); - } - - public function testMustRevalidateWithMustRevalidateCacheControlHeader() - { - $response = new Response(); - $response->headers->set('cache-control', 'must-revalidate'); - - $this->assertTrue($response->mustRevalidate()); - } - - public function testMustRevalidateWithProxyRevalidateCacheControlHeader() - { - $response = new Response(); - $response->headers->set('cache-control', 'proxy-revalidate'); - - $this->assertTrue($response->mustRevalidate()); - } - - public function testSetNotModified() - { - $response = new Response('foo'); - $modified = $response->setNotModified(); - $this->assertObjectHasAttribute('headers', $modified); - $this->assertObjectHasAttribute('content', $modified); - $this->assertObjectHasAttribute('version', $modified); - $this->assertObjectHasAttribute('statusCode', $modified); - $this->assertObjectHasAttribute('statusText', $modified); - $this->assertObjectHasAttribute('charset', $modified); - $this->assertEquals(304, $modified->getStatusCode()); - - ob_start(); - $modified->sendContent(); - $string = ob_get_clean(); - $this->assertEmpty($string); - } - - public function testIsSuccessful() - { - $response = new Response(); - $this->assertTrue($response->isSuccessful()); - } - - public function testIsNotModified() - { - $response = new Response(); - $modified = $response->isNotModified(new Request()); - $this->assertFalse($modified); - } - - public function testIsNotModifiedNotSafe() - { - $request = Request::create('/homepage', 'POST'); - - $response = new Response(); - $this->assertFalse($response->isNotModified($request)); - } - - public function testIsNotModifiedLastModified() - { - $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; - $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; - $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; - - $request = new Request(); - $request->headers->set('If-Modified-Since', $modified); - - $response = new Response(); - - $response->headers->set('Last-Modified', $modified); - $this->assertTrue($response->isNotModified($request)); - - $response->headers->set('Last-Modified', $before); - $this->assertTrue($response->isNotModified($request)); - - $response->headers->set('Last-Modified', $after); - $this->assertFalse($response->isNotModified($request)); - - $response->headers->set('Last-Modified', ''); - $this->assertFalse($response->isNotModified($request)); - } - - public function testIsNotModifiedEtag() - { - $etagOne = 'randomly_generated_etag'; - $etagTwo = 'randomly_generated_etag_2'; - - $request = new Request(); - $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); - - $response = new Response(); - - $response->headers->set('ETag', $etagOne); - $this->assertTrue($response->isNotModified($request)); - - $response->headers->set('ETag', $etagTwo); - $this->assertTrue($response->isNotModified($request)); - - $response->headers->set('ETag', ''); - $this->assertFalse($response->isNotModified($request)); - } - - public function testIsNotModifiedLastModifiedAndEtag() - { - $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; - $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; - $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; - $etag = 'randomly_generated_etag'; - - $request = new Request(); - $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); - $request->headers->set('If-Modified-Since', $modified); - - $response = new Response(); - - $response->headers->set('ETag', $etag); - $response->headers->set('Last-Modified', $after); - $this->assertFalse($response->isNotModified($request)); - - $response->headers->set('ETag', 'non-existent-etag'); - $response->headers->set('Last-Modified', $before); - $this->assertFalse($response->isNotModified($request)); - - $response->headers->set('ETag', $etag); - $response->headers->set('Last-Modified', $modified); - $this->assertTrue($response->isNotModified($request)); - } - - public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified() - { - $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; - $etag = 'randomly_generated_etag'; - - $request = new Request(); - $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); - $request->headers->set('If-Modified-Since', $modified); - - $response = new Response(); - - $response->headers->set('ETag', $etag); - $this->assertTrue($response->isNotModified($request)); - - $response->headers->set('ETag', 'non-existent-etag'); - $this->assertFalse($response->isNotModified($request)); - } - - public function testIsValidateable() - { - $response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); - $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present'); - - $response = new Response('', 200, array('ETag' => '"12345"')); - $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present'); - - $response = new Response(); - $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present'); - } - - public function testGetDate() - { - $oneHourAgo = $this->createDateTimeOneHourAgo(); - $response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822))); - $date = $response->getDate(); - $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present'); - - $response = new Response(); - $date = $response->getDate(); - $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present'); - - $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); - $now = $this->createDateTimeNow(); - $response->headers->set('Date', $now->format(DATE_RFC2822)); - $date = $response->getDate(); - $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified'); - - $response = new Response('', 200); - $now = $this->createDateTimeNow(); - $response->headers->remove('Date'); - $date = $response->getDate(); - $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed'); - } - - public function testGetMaxAge() - { - $response = new Response(); - $response->headers->set('Cache-Control', 's-maxage=600, max-age=0'); - $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'max-age=600'); - $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'must-revalidate'); - $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); - $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'must-revalidate'); - $response->headers->set('Expires', -1); - $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822)); - - $response = new Response(); - $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available'); - } - - public function testSetSharedMaxAge() - { - $response = new Response(); - $response->setSharedMaxAge(20); - - $cacheControl = $response->headers->get('Cache-Control'); - $this->assertEquals('public, s-maxage=20', $cacheControl); - } - - public function testIsPrivate() - { - $response = new Response(); - $response->headers->set('Cache-Control', 'max-age=100'); - $response->setPrivate(); - $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); - $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'public, max-age=100'); - $response->setPrivate(); - $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); - $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); - $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive'); - } - - public function testExpire() - { - $response = new Response(); - $response->headers->set('Cache-Control', 'max-age=100'); - $response->expire(); - $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500'); - $response->expire(); - $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500'); - $response->headers->set('Age', '1000'); - $response->expire(); - $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired'); - - $response = new Response(); - $response->expire(); - $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information'); - - $response = new Response(); - $response->headers->set('Expires', -1); - $response->expire(); - $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired'); - - $response = new Response(); - $response->headers->set('Expires', date(DATE_RFC2822, time() + 600)); - $response->expire(); - $this->assertNull($response->headers->get('Expires'), '->expire() removes the Expires header when the response is fresh'); - } - - public function testGetTtl() - { - $response = new Response(); - $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present'); - - $response = new Response(); - $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); - $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present'); - - $response = new Response(); - $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)); - $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past'); - - $response = new Response(); - $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822)); - $response->headers->set('Age', 0); - $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero'); - - $response = new Response(); - $response->headers->set('Cache-Control', 'max-age=60'); - $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present'); - } - - public function testSetClientTtl() - { - $response = new Response(); - $response->setClientTtl(10); - - $this->assertEquals($response->getMaxAge(), $response->getAge() + 10); - } - - public function testGetSetProtocolVersion() - { - $response = new Response(); - - $this->assertEquals('1.0', $response->getProtocolVersion()); - - $response->setProtocolVersion('1.1'); - - $this->assertEquals('1.1', $response->getProtocolVersion()); - } - - public function testGetVary() - { - $response = new Response(); - $this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present'); - - $response = new Response(); - $response->headers->set('Vary', 'Accept-Language'); - $this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value'); - - $response = new Response(); - $response->headers->set('Vary', 'Accept-Language User-Agent X-Foo'); - $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces'); - - $response = new Response(); - $response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo'); - $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas'); - - $vary = array('Accept-Language', 'User-Agent', 'X-foo'); - - $response = new Response(); - $response->headers->set('Vary', $vary); - $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); - - $response = new Response(); - $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo'); - $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); - } - - public function testSetVary() - { - $response = new Response(); - $response->setVary('Accept-Language'); - $this->assertEquals(array('Accept-Language'), $response->getVary()); - - $response->setVary('Accept-Language, User-Agent'); - $this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default'); - - $response->setVary('X-Foo', false); - $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false'); - } - - public function testDefaultContentType() - { - $headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(array('set'))->getMock(); - $headerMock->expects($this->at(0)) - ->method('set') - ->with('Content-Type', 'text/html'); - $headerMock->expects($this->at(1)) - ->method('set') - ->with('Content-Type', 'text/html; charset=UTF-8'); - - $response = new Response('foo'); - $response->headers = $headerMock; - - $response->prepare(new Request()); - } - - public function testContentTypeCharset() - { - $response = new Response(); - $response->headers->set('Content-Type', 'text/css'); - - // force fixContentType() to be called - $response->prepare(new Request()); - - $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type')); - } - - public function testPrepareDoesNothingIfContentTypeIsSet() - { - $response = new Response('foo'); - $response->headers->set('Content-Type', 'text/plain'); - - $response->prepare(new Request()); - - $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type')); - } - - public function testPrepareDoesNothingIfRequestFormatIsNotDefined() - { - $response = new Response('foo'); - - $response->prepare(new Request()); - - $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type')); - } - - public function testPrepareSetContentType() - { - $response = new Response('foo'); - $request = Request::create('/'); - $request->setRequestFormat('json'); - - $response->prepare($request); - - $this->assertEquals('application/json', $response->headers->get('content-type')); - } - - public function testPrepareRemovesContentForHeadRequests() - { - $response = new Response('foo'); - $request = Request::create('/', 'HEAD'); - - $length = 12345; - $response->headers->set('Content-Length', $length); - $response->prepare($request); - - $this->assertEquals('', $response->getContent()); - $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13'); - } - - public function testPrepareRemovesContentForInformationalResponse() - { - $response = new Response('foo'); - $request = Request::create('/'); - - $response->setContent('content'); - $response->setStatusCode(101); - $response->prepare($request); - $this->assertEquals('', $response->getContent()); - $this->assertFalse($response->headers->has('Content-Type')); - $this->assertFalse($response->headers->has('Content-Type')); - - $response->setContent('content'); - $response->setStatusCode(304); - $response->prepare($request); - $this->assertEquals('', $response->getContent()); - $this->assertFalse($response->headers->has('Content-Type')); - $this->assertFalse($response->headers->has('Content-Length')); - } - - public function testPrepareRemovesContentLength() - { - $response = new Response('foo'); - $request = Request::create('/'); - - $response->headers->set('Content-Length', 12345); - $response->prepare($request); - $this->assertEquals(12345, $response->headers->get('Content-Length')); - - $response->headers->set('Transfer-Encoding', 'chunked'); - $response->prepare($request); - $this->assertFalse($response->headers->has('Content-Length')); - } - - public function testPrepareSetsPragmaOnHttp10Only() - { - $request = Request::create('/', 'GET'); - $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); - - $response = new Response('foo'); - $response->prepare($request); - $this->assertEquals('no-cache', $response->headers->get('pragma')); - $this->assertEquals('-1', $response->headers->get('expires')); - - $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); - $response = new Response('foo'); - $response->prepare($request); - $this->assertFalse($response->headers->has('pragma')); - $this->assertFalse($response->headers->has('expires')); - } - - public function testSetCache() - { - $response = new Response(); - //array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public') - try { - $response->setCache(array('wrong option' => 'value')); - $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported'); - } catch (\Exception $e) { - $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported'); - $this->assertContains('"wrong option"', $e->getMessage()); - } - - $options = array('etag' => '"whatever"'); - $response->setCache($options); - $this->assertEquals($response->getEtag(), '"whatever"'); - - $now = $this->createDateTimeNow(); - $options = array('last_modified' => $now); - $response->setCache($options); - $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp()); - - $options = array('max_age' => 100); - $response->setCache($options); - $this->assertEquals($response->getMaxAge(), 100); - - $options = array('s_maxage' => 200); - $response->setCache($options); - $this->assertEquals($response->getMaxAge(), 200); - - $this->assertTrue($response->headers->hasCacheControlDirective('public')); - $this->assertFalse($response->headers->hasCacheControlDirective('private')); - - $response->setCache(array('public' => true)); - $this->assertTrue($response->headers->hasCacheControlDirective('public')); - $this->assertFalse($response->headers->hasCacheControlDirective('private')); - - $response->setCache(array('public' => false)); - $this->assertFalse($response->headers->hasCacheControlDirective('public')); - $this->assertTrue($response->headers->hasCacheControlDirective('private')); - - $response->setCache(array('private' => true)); - $this->assertFalse($response->headers->hasCacheControlDirective('public')); - $this->assertTrue($response->headers->hasCacheControlDirective('private')); - - $response->setCache(array('private' => false)); - $this->assertTrue($response->headers->hasCacheControlDirective('public')); - $this->assertFalse($response->headers->hasCacheControlDirective('private')); - - $response->setCache(array('immutable' => true)); - $this->assertTrue($response->headers->hasCacheControlDirective('immutable')); - - $response->setCache(array('immutable' => false)); - $this->assertFalse($response->headers->hasCacheControlDirective('immutable')); - } - - public function testSendContent() - { - $response = new Response('test response rendering', 200); - - ob_start(); - $response->sendContent(); - $string = ob_get_clean(); - $this->assertContains('test response rendering', $string); - } - - public function testSetPublic() - { - $response = new Response(); - $response->setPublic(); - - $this->assertTrue($response->headers->hasCacheControlDirective('public')); - $this->assertFalse($response->headers->hasCacheControlDirective('private')); - } - - public function testSetImmutable() - { - $response = new Response(); - $response->setImmutable(); - - $this->assertTrue($response->headers->hasCacheControlDirective('immutable')); - } - - public function testIsImmutable() - { - $response = new Response(); - $response->setImmutable(); - - $this->assertTrue($response->isImmutable()); - } - - public function testSetExpires() - { - $response = new Response(); - $response->setExpires(null); - - $this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null'); - - $now = $this->createDateTimeNow(); - $response->setExpires($now); - - $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp()); - } - - public function testSetLastModified() - { - $response = new Response(); - $response->setLastModified($this->createDateTimeNow()); - $this->assertNotNull($response->getLastModified()); - - $response->setLastModified(null); - $this->assertNull($response->getLastModified()); - } - - public function testIsInvalid() - { - $response = new Response(); - - try { - $response->setStatusCode(99); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertTrue($response->isInvalid()); - } - - try { - $response->setStatusCode(650); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertTrue($response->isInvalid()); - } - - $response = new Response('', 200); - $this->assertFalse($response->isInvalid()); - } - - /** - * @dataProvider getStatusCodeFixtures - */ - public function testSetStatusCode($code, $text, $expectedText) - { - $response = new Response(); - - $response->setStatusCode($code, $text); - - $statusText = new \ReflectionProperty($response, 'statusText'); - $statusText->setAccessible(true); - - $this->assertEquals($expectedText, $statusText->getValue($response)); - } - - public function getStatusCodeFixtures() - { - return array( - array('200', null, 'OK'), - array('200', false, ''), - array('200', 'foo', 'foo'), - array('199', null, 'unknown status'), - array('199', false, ''), - array('199', 'foo', 'foo'), - ); - } - - public function testIsInformational() - { - $response = new Response('', 100); - $this->assertTrue($response->isInformational()); - - $response = new Response('', 200); - $this->assertFalse($response->isInformational()); - } - - public function testIsRedirectRedirection() - { - foreach (array(301, 302, 303, 307) as $code) { - $response = new Response('', $code); - $this->assertTrue($response->isRedirection()); - $this->assertTrue($response->isRedirect()); - } - - $response = new Response('', 304); - $this->assertTrue($response->isRedirection()); - $this->assertFalse($response->isRedirect()); - - $response = new Response('', 200); - $this->assertFalse($response->isRedirection()); - $this->assertFalse($response->isRedirect()); - - $response = new Response('', 404); - $this->assertFalse($response->isRedirection()); - $this->assertFalse($response->isRedirect()); - - $response = new Response('', 301, array('Location' => '/good-uri')); - $this->assertFalse($response->isRedirect('/bad-uri')); - $this->assertTrue($response->isRedirect('/good-uri')); - } - - public function testIsNotFound() - { - $response = new Response('', 404); - $this->assertTrue($response->isNotFound()); - - $response = new Response('', 200); - $this->assertFalse($response->isNotFound()); - } - - public function testIsEmpty() - { - foreach (array(204, 304) as $code) { - $response = new Response('', $code); - $this->assertTrue($response->isEmpty()); - } - - $response = new Response('', 200); - $this->assertFalse($response->isEmpty()); - } - - public function testIsForbidden() - { - $response = new Response('', 403); - $this->assertTrue($response->isForbidden()); - - $response = new Response('', 200); - $this->assertFalse($response->isForbidden()); - } - - public function testIsOk() - { - $response = new Response('', 200); - $this->assertTrue($response->isOk()); - - $response = new Response('', 404); - $this->assertFalse($response->isOk()); - } - - public function testIsServerOrClientError() - { - $response = new Response('', 404); - $this->assertTrue($response->isClientError()); - $this->assertFalse($response->isServerError()); - - $response = new Response('', 500); - $this->assertFalse($response->isClientError()); - $this->assertTrue($response->isServerError()); - } - - public function testHasVary() - { - $response = new Response(); - $this->assertFalse($response->hasVary()); - - $response->setVary('User-Agent'); - $this->assertTrue($response->hasVary()); - } - - public function testSetEtag() - { - $response = new Response('', 200, array('ETag' => '"12345"')); - $response->setEtag(); - - $this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null'); - } - - /** - * @dataProvider validContentProvider - */ - public function testSetContent($content) - { - $response = new Response(); - $response->setContent($content); - $this->assertEquals((string) $content, $response->getContent()); - } - - /** - * @expectedException \UnexpectedValueException - * @dataProvider invalidContentProvider - */ - public function testSetContentInvalid($content) - { - $response = new Response(); - $response->setContent($content); - } - - public function testSettersAreChainable() - { - $response = new Response(); - - $setters = array( - 'setProtocolVersion' => '1.0', - 'setCharset' => 'UTF-8', - 'setPublic' => null, - 'setPrivate' => null, - 'setDate' => $this->createDateTimeNow(), - 'expire' => null, - 'setMaxAge' => 1, - 'setSharedMaxAge' => 1, - 'setTtl' => 1, - 'setClientTtl' => 1, - ); - - foreach ($setters as $setter => $arg) { - $this->assertEquals($response, $response->{$setter}($arg)); - } - } - - public function testNoDeprecationsAreTriggered() - { - new DefaultResponse(); - $this->getMockBuilder(Response::class)->getMock(); - - // we just need to ensure that subclasses of Response can be created without any deprecations - // being triggered if the subclass does not override any final methods - $this->addToAssertionCount(1); - } - - public function validContentProvider() - { - return array( - 'obj' => array(new StringableObject()), - 'string' => array('Foo'), - 'int' => array(2), - ); - } - - public function invalidContentProvider() - { - return array( - 'obj' => array(new \stdClass()), - 'array' => array(array()), - 'bool' => array(true, '1'), - ); - } - - protected function createDateTimeOneHourAgo() - { - return $this->createDateTimeNow()->sub(new \DateInterval('PT1H')); - } - - protected function createDateTimeOneHourLater() - { - return $this->createDateTimeNow()->add(new \DateInterval('PT1H')); - } - - protected function createDateTimeNow() - { - $date = new \DateTime(); - - return $date->setTimestamp(time()); - } - - protected function provideResponse() - { - return new Response(); - } - - /** - * @see http://github.com/zendframework/zend-diactoros for the canonical source repository - * - * @author Fábio Pacheco - * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com) - * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License - */ - public function ianaCodesReasonPhrasesProvider() - { - if (!\in_array('https', stream_get_wrappers(), true)) { - $this->markTestSkipped('The "https" wrapper is not available'); - } - - $ianaHttpStatusCodes = new \DOMDocument(); - - libxml_set_streams_context(stream_context_create(array( - 'http' => array( - 'method' => 'GET', - 'timeout' => 30, - ), - ))); - - $ianaHttpStatusCodes->load('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml'); - if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) { - self::fail('Invalid IANA\'s HTTP status code list.'); - } - - $ianaCodesReasonPhrases = array(); - - $xpath = new \DOMXPath($ianaHttpStatusCodes); - $xpath->registerNamespace('ns', 'http://www.iana.org/assignments'); - - $records = $xpath->query('//ns:record'); - foreach ($records as $record) { - $value = $xpath->query('.//ns:value', $record)->item(0)->nodeValue; - $description = $xpath->query('.//ns:description', $record)->item(0)->nodeValue; - - if (\in_array($description, array('Unassigned', '(Unused)'), true)) { - continue; - } - - if (preg_match('/^([0-9]+)\s*\-\s*([0-9]+)$/', $value, $matches)) { - for ($value = $matches[1]; $value <= $matches[2]; ++$value) { - $ianaCodesReasonPhrases[] = array($value, $description); - } - } else { - $ianaCodesReasonPhrases[] = array($value, $description); - } - } - - return $ianaCodesReasonPhrases; - } - - /** - * @dataProvider ianaCodesReasonPhrasesProvider - */ - public function testReasonPhraseDefaultsAgainstIana($code, $reasonPhrase) - { - $this->assertEquals($reasonPhrase, Response::$statusTexts[$code]); - } -} - -class StringableObject -{ - public function __toString() - { - return 'Foo'; - } -} - -class DefaultResponse extends Response -{ -} - -class ExtendedResponse extends Response -{ - public function setLastModified(\DateTime $date = null) - { - } - - public function getDate() - { - } -} diff --git a/vendor/symfony/http-foundation/Tests/ResponseTestCase.php b/vendor/symfony/http-foundation/Tests/ResponseTestCase.php deleted file mode 100644 index 4ead34c..0000000 --- a/vendor/symfony/http-foundation/Tests/ResponseTestCase.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; - -abstract class ResponseTestCase extends TestCase -{ - public function testNoCacheControlHeaderOnAttachmentUsingHTTPSAndMSIE() - { - // Check for HTTPS and IE 8 - $request = new Request(); - $request->server->set('HTTPS', true); - $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)'); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertFalse($response->headers->has('Cache-Control')); - - // Check for IE 10 and HTTPS - $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertTrue($response->headers->has('Cache-Control')); - - // Check for IE 9 and HTTPS - $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)'); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertTrue($response->headers->has('Cache-Control')); - - // Check for IE 9 and HTTP - $request->server->set('HTTPS', false); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertTrue($response->headers->has('Cache-Control')); - - // Check for IE 8 and HTTP - $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)'); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertTrue($response->headers->has('Cache-Control')); - - // Check for non-IE and HTTPS - $request->server->set('HTTPS', true); - $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17'); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertTrue($response->headers->has('Cache-Control')); - - // Check for non-IE and HTTP - $request->server->set('HTTPS', false); - - $response = $this->provideResponse(); - $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); - $response->prepare($request); - - $this->assertTrue($response->headers->has('Cache-Control')); - } - - abstract protected function provideResponse(); -} diff --git a/vendor/symfony/http-foundation/Tests/ServerBagTest.php b/vendor/symfony/http-foundation/Tests/ServerBagTest.php deleted file mode 100644 index f8becec..0000000 --- a/vendor/symfony/http-foundation/Tests/ServerBagTest.php +++ /dev/null @@ -1,170 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\ServerBag; - -/** - * ServerBagTest. - * - * @author Bulat Shakirzyanov - */ -class ServerBagTest extends TestCase -{ - public function testShouldExtractHeadersFromServerArray() - { - $server = array( - 'SOME_SERVER_VARIABLE' => 'value', - 'SOME_SERVER_VARIABLE2' => 'value', - 'ROOT' => 'value', - 'HTTP_CONTENT_TYPE' => 'text/html', - 'HTTP_CONTENT_LENGTH' => '0', - 'HTTP_ETAG' => 'asdf', - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => 'bar', - ); - - $bag = new ServerBag($server); - - $this->assertEquals(array( - 'CONTENT_TYPE' => 'text/html', - 'CONTENT_LENGTH' => '0', - 'ETAG' => 'asdf', - 'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'), - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => 'bar', - ), $bag->getHeaders()); - } - - public function testHttpPasswordIsOptional() - { - $bag = new ServerBag(array('PHP_AUTH_USER' => 'foo')); - - $this->assertEquals(array( - 'AUTHORIZATION' => 'Basic '.base64_encode('foo:'), - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => '', - ), $bag->getHeaders()); - } - - public function testHttpBasicAuthWithPhpCgi() - { - $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'))); - - $this->assertEquals(array( - 'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'), - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => 'bar', - ), $bag->getHeaders()); - } - - public function testHttpBasicAuthWithPhpCgiBogus() - { - $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar'))); - - // Username and passwords should not be set as the header is bogus - $headers = $bag->getHeaders(); - $this->assertArrayNotHasKey('PHP_AUTH_USER', $headers); - $this->assertArrayNotHasKey('PHP_AUTH_PW', $headers); - } - - public function testHttpBasicAuthWithPhpCgiRedirect() - { - $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'))); - - $this->assertEquals(array( - 'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'), - 'PHP_AUTH_USER' => 'username', - 'PHP_AUTH_PW' => 'pass:word', - ), $bag->getHeaders()); - } - - public function testHttpBasicAuthWithPhpCgiEmptyPassword() - { - $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:'))); - - $this->assertEquals(array( - 'AUTHORIZATION' => 'Basic '.base64_encode('foo:'), - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => '', - ), $bag->getHeaders()); - } - - public function testHttpDigestAuthWithPhpCgi() - { - $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; - $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest)); - - $this->assertEquals(array( - 'AUTHORIZATION' => $digest, - 'PHP_AUTH_DIGEST' => $digest, - ), $bag->getHeaders()); - } - - public function testHttpDigestAuthWithPhpCgiBogus() - { - $digest = 'Digest_username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; - $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest)); - - // Username and passwords should not be set as the header is bogus - $headers = $bag->getHeaders(); - $this->assertArrayNotHasKey('PHP_AUTH_USER', $headers); - $this->assertArrayNotHasKey('PHP_AUTH_PW', $headers); - } - - public function testHttpDigestAuthWithPhpCgiRedirect() - { - $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; - $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest)); - - $this->assertEquals(array( - 'AUTHORIZATION' => $digest, - 'PHP_AUTH_DIGEST' => $digest, - ), $bag->getHeaders()); - } - - public function testOAuthBearerAuth() - { - $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; - $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent)); - - $this->assertEquals(array( - 'AUTHORIZATION' => $headerContent, - ), $bag->getHeaders()); - } - - public function testOAuthBearerAuthWithRedirect() - { - $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; - $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent)); - - $this->assertEquals(array( - 'AUTHORIZATION' => $headerContent, - ), $bag->getHeaders()); - } - - /** - * @see https://github.com/symfony/symfony/issues/17345 - */ - public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet() - { - $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; - $bag = new ServerBag(array('PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent)); - - $this->assertEquals(array( - 'AUTHORIZATION' => $headerContent, - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => '', - ), $bag->getHeaders()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php deleted file mode 100644 index 43644e2..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php +++ /dev/null @@ -1,186 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; - -/** - * Tests AttributeBag. - * - * @author Drak - */ -class AttributeBagTest extends TestCase -{ - private $array = array(); - - /** - * @var AttributeBag - */ - private $bag; - - protected function setUp() - { - $this->array = array( - 'hello' => 'world', - 'always' => 'be happy', - 'user.login' => 'drak', - 'csrf.token' => array( - 'a' => '1234', - 'b' => '4321', - ), - 'category' => array( - 'fishing' => array( - 'first' => 'cod', - 'second' => 'sole', - ), - ), - ); - $this->bag = new AttributeBag('_sf2'); - $this->bag->initialize($this->array); - } - - protected function tearDown() - { - $this->bag = null; - $this->array = array(); - } - - public function testInitialize() - { - $bag = new AttributeBag(); - $bag->initialize($this->array); - $this->assertEquals($this->array, $bag->all()); - $array = array('should' => 'change'); - $bag->initialize($array); - $this->assertEquals($array, $bag->all()); - } - - public function testGetStorageKey() - { - $this->assertEquals('_sf2', $this->bag->getStorageKey()); - $attributeBag = new AttributeBag('test'); - $this->assertEquals('test', $attributeBag->getStorageKey()); - } - - public function testGetSetName() - { - $this->assertEquals('attributes', $this->bag->getName()); - $this->bag->setName('foo'); - $this->assertEquals('foo', $this->bag->getName()); - } - - /** - * @dataProvider attributesProvider - */ - public function testHas($key, $value, $exists) - { - $this->assertEquals($exists, $this->bag->has($key)); - } - - /** - * @dataProvider attributesProvider - */ - public function testGet($key, $value, $expected) - { - $this->assertEquals($value, $this->bag->get($key)); - } - - public function testGetDefaults() - { - $this->assertNull($this->bag->get('user2.login')); - $this->assertEquals('default', $this->bag->get('user2.login', 'default')); - } - - /** - * @dataProvider attributesProvider - */ - public function testSet($key, $value, $expected) - { - $this->bag->set($key, $value); - $this->assertEquals($value, $this->bag->get($key)); - } - - public function testAll() - { - $this->assertEquals($this->array, $this->bag->all()); - - $this->bag->set('hello', 'fabien'); - $array = $this->array; - $array['hello'] = 'fabien'; - $this->assertEquals($array, $this->bag->all()); - } - - public function testReplace() - { - $array = array(); - $array['name'] = 'jack'; - $array['foo.bar'] = 'beep'; - $this->bag->replace($array); - $this->assertEquals($array, $this->bag->all()); - $this->assertNull($this->bag->get('hello')); - $this->assertNull($this->bag->get('always')); - $this->assertNull($this->bag->get('user.login')); - } - - public function testRemove() - { - $this->assertEquals('world', $this->bag->get('hello')); - $this->bag->remove('hello'); - $this->assertNull($this->bag->get('hello')); - - $this->assertEquals('be happy', $this->bag->get('always')); - $this->bag->remove('always'); - $this->assertNull($this->bag->get('always')); - - $this->assertEquals('drak', $this->bag->get('user.login')); - $this->bag->remove('user.login'); - $this->assertNull($this->bag->get('user.login')); - } - - public function testClear() - { - $this->bag->clear(); - $this->assertEquals(array(), $this->bag->all()); - } - - public function attributesProvider() - { - return array( - array('hello', 'world', true), - array('always', 'be happy', true), - array('user.login', 'drak', true), - array('csrf.token', array('a' => '1234', 'b' => '4321'), true), - array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true), - array('user2.login', null, false), - array('never', null, false), - array('bye', null, false), - array('bye/for/now', null, false), - ); - } - - public function testGetIterator() - { - $i = 0; - foreach ($this->bag as $key => $val) { - $this->assertEquals($this->array[$key], $val); - ++$i; - } - - $this->assertEquals(\count($this->array), $i); - } - - public function testCount() - { - $this->assertCount(\count($this->array), $this->bag); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php deleted file mode 100644 index ec4cd5a..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php +++ /dev/null @@ -1,204 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; - -/** - * Tests NamespacedAttributeBag. - * - * @author Drak - */ -class NamespacedAttributeBagTest extends TestCase -{ - private $array = array(); - - /** - * @var NamespacedAttributeBag - */ - private $bag; - - protected function setUp() - { - $this->array = array( - 'hello' => 'world', - 'always' => 'be happy', - 'user.login' => 'drak', - 'csrf.token' => array( - 'a' => '1234', - 'b' => '4321', - ), - 'category' => array( - 'fishing' => array( - 'first' => 'cod', - 'second' => 'sole', - ), - ), - ); - $this->bag = new NamespacedAttributeBag('_sf2', '/'); - $this->bag->initialize($this->array); - } - - protected function tearDown() - { - $this->bag = null; - $this->array = array(); - } - - public function testInitialize() - { - $bag = new NamespacedAttributeBag(); - $bag->initialize($this->array); - $this->assertEquals($this->array, $this->bag->all()); - $array = array('should' => 'not stick'); - $bag->initialize($array); - - // should have remained the same - $this->assertEquals($this->array, $this->bag->all()); - } - - public function testGetStorageKey() - { - $this->assertEquals('_sf2', $this->bag->getStorageKey()); - $attributeBag = new NamespacedAttributeBag('test'); - $this->assertEquals('test', $attributeBag->getStorageKey()); - } - - /** - * @dataProvider attributesProvider - */ - public function testHas($key, $value, $exists) - { - $this->assertEquals($exists, $this->bag->has($key)); - } - - /** - * @dataProvider attributesProvider - */ - public function testHasNoSideEffect($key, $value, $expected) - { - $expected = json_encode($this->bag->all()); - $this->bag->has($key); - - $this->assertEquals($expected, json_encode($this->bag->all())); - } - - /** - * @dataProvider attributesProvider - */ - public function testGet($key, $value, $expected) - { - $this->assertEquals($value, $this->bag->get($key)); - } - - public function testGetDefaults() - { - $this->assertNull($this->bag->get('user2.login')); - $this->assertEquals('default', $this->bag->get('user2.login', 'default')); - } - - /** - * @dataProvider attributesProvider - */ - public function testGetNoSideEffect($key, $value, $expected) - { - $expected = json_encode($this->bag->all()); - $this->bag->get($key); - - $this->assertEquals($expected, json_encode($this->bag->all())); - } - - /** - * @dataProvider attributesProvider - */ - public function testSet($key, $value, $expected) - { - $this->bag->set($key, $value); - $this->assertEquals($value, $this->bag->get($key)); - } - - public function testAll() - { - $this->assertEquals($this->array, $this->bag->all()); - - $this->bag->set('hello', 'fabien'); - $array = $this->array; - $array['hello'] = 'fabien'; - $this->assertEquals($array, $this->bag->all()); - } - - public function testReplace() - { - $array = array(); - $array['name'] = 'jack'; - $array['foo.bar'] = 'beep'; - $this->bag->replace($array); - $this->assertEquals($array, $this->bag->all()); - $this->assertNull($this->bag->get('hello')); - $this->assertNull($this->bag->get('always')); - $this->assertNull($this->bag->get('user.login')); - } - - public function testRemove() - { - $this->assertEquals('world', $this->bag->get('hello')); - $this->bag->remove('hello'); - $this->assertNull($this->bag->get('hello')); - - $this->assertEquals('be happy', $this->bag->get('always')); - $this->bag->remove('always'); - $this->assertNull($this->bag->get('always')); - - $this->assertEquals('drak', $this->bag->get('user.login')); - $this->bag->remove('user.login'); - $this->assertNull($this->bag->get('user.login')); - } - - public function testRemoveExistingNamespacedAttribute() - { - $this->assertSame('cod', $this->bag->remove('category/fishing/first')); - } - - public function testRemoveNonexistingNamespacedAttribute() - { - $this->assertNull($this->bag->remove('foo/bar/baz')); - } - - public function testClear() - { - $this->bag->clear(); - $this->assertEquals(array(), $this->bag->all()); - } - - public function attributesProvider() - { - return array( - array('hello', 'world', true), - array('always', 'be happy', true), - array('user.login', 'drak', true), - array('csrf.token', array('a' => '1234', 'b' => '4321'), true), - array('csrf.token/a', '1234', true), - array('csrf.token/b', '4321', true), - array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true), - array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true), - array('category/fishing/missing/first', null, false), - array('category/fishing/first', 'cod', true), - array('category/fishing/second', 'sole', true), - array('category/fishing/missing/second', null, false), - array('user2.login', null, false), - array('never', null, false), - array('bye', null, false), - array('bye/for/now', null, false), - ); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php deleted file mode 100644 index fa8626a..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php +++ /dev/null @@ -1,161 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Flash; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag; - -/** - * AutoExpireFlashBagTest. - * - * @author Drak - */ -class AutoExpireFlashBagTest extends TestCase -{ - /** - * @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag - */ - private $bag; - - protected $array = array(); - - protected function setUp() - { - parent::setUp(); - $this->bag = new FlashBag(); - $this->array = array('new' => array('notice' => array('A previous flash message'))); - $this->bag->initialize($this->array); - } - - protected function tearDown() - { - $this->bag = null; - parent::tearDown(); - } - - public function testInitialize() - { - $bag = new FlashBag(); - $array = array('new' => array('notice' => array('A previous flash message'))); - $bag->initialize($array); - $this->assertEquals(array('A previous flash message'), $bag->peek('notice')); - $array = array('new' => array( - 'notice' => array('Something else'), - 'error' => array('a'), - )); - $bag->initialize($array); - $this->assertEquals(array('Something else'), $bag->peek('notice')); - $this->assertEquals(array('a'), $bag->peek('error')); - } - - public function testGetStorageKey() - { - $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey()); - $attributeBag = new FlashBag('test'); - $this->assertEquals('test', $attributeBag->getStorageKey()); - } - - public function testGetSetName() - { - $this->assertEquals('flashes', $this->bag->getName()); - $this->bag->setName('foo'); - $this->assertEquals('foo', $this->bag->getName()); - } - - public function testPeek() - { - $this->assertEquals(array(), $this->bag->peek('non_existing')); - $this->assertEquals(array('default'), $this->bag->peek('non_existing', array('default'))); - $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); - $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); - } - - public function testSet() - { - $this->bag->set('notice', 'Foo'); - $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); - } - - public function testHas() - { - $this->assertFalse($this->bag->has('nothing')); - $this->assertTrue($this->bag->has('notice')); - } - - public function testKeys() - { - $this->assertEquals(array('notice'), $this->bag->keys()); - } - - public function testPeekAll() - { - $array = array( - 'new' => array( - 'notice' => 'Foo', - 'error' => 'Bar', - ), - ); - - $this->bag->initialize($array); - $this->assertEquals(array( - 'notice' => 'Foo', - 'error' => 'Bar', - ), $this->bag->peekAll() - ); - - $this->assertEquals(array( - 'notice' => 'Foo', - 'error' => 'Bar', - ), $this->bag->peekAll() - ); - } - - public function testGet() - { - $this->assertEquals(array(), $this->bag->get('non_existing')); - $this->assertEquals(array('default'), $this->bag->get('non_existing', array('default'))); - $this->assertEquals(array('A previous flash message'), $this->bag->get('notice')); - $this->assertEquals(array(), $this->bag->get('notice')); - } - - public function testSetAll() - { - $this->bag->setAll(array('a' => 'first', 'b' => 'second')); - $this->assertFalse($this->bag->has('a')); - $this->assertFalse($this->bag->has('b')); - } - - public function testAll() - { - $this->bag->set('notice', 'Foo'); - $this->bag->set('error', 'Bar'); - $this->assertEquals(array( - 'notice' => array('A previous flash message'), - ), $this->bag->all() - ); - - $this->assertEquals(array(), $this->bag->all()); - } - - public function testClear() - { - $this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear()); - } - - public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes() - { - $this->bag->add('success', 'Something'); - $this->bag->all(); - - $this->assertEquals(array('new' => array('success' => array('Something')), 'display' => array()), $this->array); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php deleted file mode 100644 index 905a1f7..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php +++ /dev/null @@ -1,157 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Flash; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; - -/** - * FlashBagTest. - * - * @author Drak - */ -class FlashBagTest extends TestCase -{ - /** - * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface - */ - private $bag; - - protected $array = array(); - - protected function setUp() - { - parent::setUp(); - $this->bag = new FlashBag(); - $this->array = array('notice' => array('A previous flash message')); - $this->bag->initialize($this->array); - } - - protected function tearDown() - { - $this->bag = null; - parent::tearDown(); - } - - public function testInitialize() - { - $bag = new FlashBag(); - $bag->initialize($this->array); - $this->assertEquals($this->array, $bag->peekAll()); - $array = array('should' => array('change')); - $bag->initialize($array); - $this->assertEquals($array, $bag->peekAll()); - } - - public function testGetStorageKey() - { - $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey()); - $attributeBag = new FlashBag('test'); - $this->assertEquals('test', $attributeBag->getStorageKey()); - } - - public function testGetSetName() - { - $this->assertEquals('flashes', $this->bag->getName()); - $this->bag->setName('foo'); - $this->assertEquals('foo', $this->bag->getName()); - } - - public function testPeek() - { - $this->assertEquals(array(), $this->bag->peek('non_existing')); - $this->assertEquals(array('default'), $this->bag->peek('not_existing', array('default'))); - $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); - $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); - } - - public function testAdd() - { - $tab = array('bar' => 'baz'); - $this->bag->add('string_message', 'lorem'); - $this->bag->add('object_message', new \stdClass()); - $this->bag->add('array_message', $tab); - - $this->assertEquals(array('lorem'), $this->bag->get('string_message')); - $this->assertEquals(array(new \stdClass()), $this->bag->get('object_message')); - $this->assertEquals(array($tab), $this->bag->get('array_message')); - } - - public function testGet() - { - $this->assertEquals(array(), $this->bag->get('non_existing')); - $this->assertEquals(array('default'), $this->bag->get('not_existing', array('default'))); - $this->assertEquals(array('A previous flash message'), $this->bag->get('notice')); - $this->assertEquals(array(), $this->bag->get('notice')); - } - - public function testAll() - { - $this->bag->set('notice', 'Foo'); - $this->bag->set('error', 'Bar'); - $this->assertEquals(array( - 'notice' => array('Foo'), - 'error' => array('Bar'), ), $this->bag->all() - ); - - $this->assertEquals(array(), $this->bag->all()); - } - - public function testSet() - { - $this->bag->set('notice', 'Foo'); - $this->bag->set('notice', 'Bar'); - $this->assertEquals(array('Bar'), $this->bag->peek('notice')); - } - - public function testHas() - { - $this->assertFalse($this->bag->has('nothing')); - $this->assertTrue($this->bag->has('notice')); - } - - public function testKeys() - { - $this->assertEquals(array('notice'), $this->bag->keys()); - } - - public function testSetAll() - { - $this->bag->add('one_flash', 'Foo'); - $this->bag->add('another_flash', 'Bar'); - $this->assertTrue($this->bag->has('one_flash')); - $this->assertTrue($this->bag->has('another_flash')); - $this->bag->setAll(array('unique_flash' => 'FooBar')); - $this->assertFalse($this->bag->has('one_flash')); - $this->assertFalse($this->bag->has('another_flash')); - $this->assertSame(array('unique_flash' => 'FooBar'), $this->bag->all()); - $this->assertSame(array(), $this->bag->all()); - } - - public function testPeekAll() - { - $this->bag->set('notice', 'Foo'); - $this->bag->set('error', 'Bar'); - $this->assertEquals(array( - 'notice' => array('Foo'), - 'error' => array('Bar'), - ), $this->bag->peekAll() - ); - $this->assertTrue($this->bag->has('notice')); - $this->assertTrue($this->bag->has('error')); - $this->assertEquals(array( - 'notice' => array('Foo'), - 'error' => array('Bar'), - ), $this->bag->peekAll() - ); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/SessionTest.php b/vendor/symfony/http-foundation/Tests/Session/SessionTest.php deleted file mode 100644 index 63351e5..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/SessionTest.php +++ /dev/null @@ -1,263 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; - -/** - * SessionTest. - * - * @author Fabien Potencier - * @author Robert Schönthal - * @author Drak - */ -class SessionTest extends TestCase -{ - /** - * @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface - */ - protected $storage; - - /** - * @var \Symfony\Component\HttpFoundation\Session\SessionInterface - */ - protected $session; - - protected function setUp() - { - $this->storage = new MockArraySessionStorage(); - $this->session = new Session($this->storage, new AttributeBag(), new FlashBag()); - } - - protected function tearDown() - { - $this->storage = null; - $this->session = null; - } - - public function testStart() - { - $this->assertEquals('', $this->session->getId()); - $this->assertTrue($this->session->start()); - $this->assertNotEquals('', $this->session->getId()); - } - - public function testIsStarted() - { - $this->assertFalse($this->session->isStarted()); - $this->session->start(); - $this->assertTrue($this->session->isStarted()); - } - - public function testSetId() - { - $this->assertEquals('', $this->session->getId()); - $this->session->setId('0123456789abcdef'); - $this->session->start(); - $this->assertEquals('0123456789abcdef', $this->session->getId()); - } - - public function testSetIdAfterStart() - { - $this->session->start(); - $id = $this->session->getId(); - - $e = null; - try { - $this->session->setId($id); - } catch (\Exception $e) { - } - - $this->assertNull($e); - - try { - $this->session->setId('different'); - } catch (\Exception $e) { - } - - $this->assertInstanceOf('\LogicException', $e); - } - - public function testSetName() - { - $this->assertEquals('MOCKSESSID', $this->session->getName()); - $this->session->setName('session.test.com'); - $this->session->start(); - $this->assertEquals('session.test.com', $this->session->getName()); - } - - public function testGet() - { - // tests defaults - $this->assertNull($this->session->get('foo')); - $this->assertEquals(1, $this->session->get('foo', 1)); - } - - /** - * @dataProvider setProvider - */ - public function testSet($key, $value) - { - $this->session->set($key, $value); - $this->assertEquals($value, $this->session->get($key)); - } - - /** - * @dataProvider setProvider - */ - public function testHas($key, $value) - { - $this->session->set($key, $value); - $this->assertTrue($this->session->has($key)); - $this->assertFalse($this->session->has($key.'non_value')); - } - - public function testReplace() - { - $this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome')); - $this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all()); - $this->session->replace(array()); - $this->assertEquals(array(), $this->session->all()); - } - - /** - * @dataProvider setProvider - */ - public function testAll($key, $value, $result) - { - $this->session->set($key, $value); - $this->assertEquals($result, $this->session->all()); - } - - /** - * @dataProvider setProvider - */ - public function testClear($key, $value) - { - $this->session->set('hi', 'fabien'); - $this->session->set($key, $value); - $this->session->clear(); - $this->assertEquals(array(), $this->session->all()); - } - - public function setProvider() - { - return array( - array('foo', 'bar', array('foo' => 'bar')), - array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')), - array('great', 'symfony is great', array('great' => 'symfony is great')), - ); - } - - /** - * @dataProvider setProvider - */ - public function testRemove($key, $value) - { - $this->session->set('hi.world', 'have a nice day'); - $this->session->set($key, $value); - $this->session->remove($key); - $this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all()); - } - - public function testInvalidate() - { - $this->session->set('invalidate', 123); - $this->session->invalidate(); - $this->assertEquals(array(), $this->session->all()); - } - - public function testMigrate() - { - $this->session->set('migrate', 321); - $this->session->migrate(); - $this->assertEquals(321, $this->session->get('migrate')); - } - - public function testMigrateDestroy() - { - $this->session->set('migrate', 333); - $this->session->migrate(true); - $this->assertEquals(333, $this->session->get('migrate')); - } - - public function testSave() - { - $this->session->start(); - $this->session->save(); - - $this->assertFalse($this->session->isStarted()); - } - - public function testGetId() - { - $this->assertEquals('', $this->session->getId()); - $this->session->start(); - $this->assertNotEquals('', $this->session->getId()); - } - - public function testGetFlashBag() - { - $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag()); - } - - public function testGetIterator() - { - $attributes = array('hello' => 'world', 'symfony' => 'rocks'); - foreach ($attributes as $key => $val) { - $this->session->set($key, $val); - } - - $i = 0; - foreach ($this->session as $key => $val) { - $this->assertEquals($attributes[$key], $val); - ++$i; - } - - $this->assertEquals(\count($attributes), $i); - } - - public function testGetCount() - { - $this->session->set('hello', 'world'); - $this->session->set('symfony', 'rocks'); - - $this->assertCount(2, $this->session); - } - - public function testGetMeta() - { - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag()); - } - - public function testIsEmpty() - { - $this->assertTrue($this->session->isEmpty()); - - $this->session->set('hello', 'world'); - $this->assertFalse($this->session->isEmpty()); - - $this->session->remove('hello'); - $this->assertTrue($this->session->isEmpty()); - - $flash = $this->session->getFlashBag(); - $flash->set('hello', 'world'); - $this->assertFalse($this->session->isEmpty()); - - $flash->get('hello'); - $this->assertTrue($this->session->isEmpty()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php deleted file mode 100644 index 3ac081e..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; - -/** - * @requires PHP 7.0 - */ -class AbstractSessionHandlerTest extends TestCase -{ - private static $server; - - public static function setUpBeforeClass() - { - $spec = array( - 1 => array('file', '/dev/null', 'w'), - 2 => array('file', '/dev/null', 'w'), - ); - if (!self::$server = @proc_open('exec php -S localhost:8053', $spec, $pipes, __DIR__.'/Fixtures')) { - self::markTestSkipped('PHP server unable to start.'); - } - sleep(1); - } - - public static function tearDownAfterClass() - { - if (self::$server) { - proc_terminate(self::$server); - proc_close(self::$server); - } - } - - /** - * @dataProvider provideSession - */ - public function testSession($fixture) - { - $context = array('http' => array('header' => "Cookie: sid=123abc\r\n")); - $context = stream_context_create($context); - $result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context); - - $this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result); - } - - public function provideSession() - { - foreach (glob(__DIR__.'/Fixtures/*.php') as $file) { - yield array(pathinfo($file, PATHINFO_FILENAME)); - } - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc deleted file mode 100644 index 7a064c7..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc +++ /dev/null @@ -1,151 +0,0 @@ -data = $data; - } - - public function open($path, $name) - { - echo __FUNCTION__, "\n"; - - return parent::open($path, $name); - } - - public function validateId($sessionId) - { - echo __FUNCTION__, "\n"; - - return parent::validateId($sessionId); - } - - /** - * {@inheritdoc} - */ - public function read($sessionId) - { - echo __FUNCTION__, "\n"; - - return parent::read($sessionId); - } - - /** - * {@inheritdoc} - */ - public function updateTimestamp($sessionId, $data) - { - echo __FUNCTION__, "\n"; - - return true; - } - - /** - * {@inheritdoc} - */ - public function write($sessionId, $data) - { - echo __FUNCTION__, "\n"; - - return parent::write($sessionId, $data); - } - - /** - * {@inheritdoc} - */ - public function destroy($sessionId) - { - echo __FUNCTION__, "\n"; - - return parent::destroy($sessionId); - } - - public function close() - { - echo __FUNCTION__, "\n"; - - return true; - } - - public function gc($maxLifetime) - { - echo __FUNCTION__, "\n"; - - return true; - } - - protected function doRead($sessionId) - { - echo __FUNCTION__.': ', $this->data, "\n"; - - return $this->data; - } - - protected function doWrite($sessionId, $data) - { - echo __FUNCTION__.': ', $data, "\n"; - - return true; - } - - protected function doDestroy($sessionId) - { - echo __FUNCTION__, "\n"; - - return true; - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected deleted file mode 100644 index 8203714..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected +++ /dev/null @@ -1,17 +0,0 @@ -open -validateId -read -doRead: abc|i:123; -read - -write -destroy -doDestroy -close -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: max-age=10800, private, must-revalidate - [2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php deleted file mode 100644 index 3cfc125..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php +++ /dev/null @@ -1,8 +0,0 @@ - Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: max-age=10800, private, must-revalidate -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php deleted file mode 100644 index 3e62fb9..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php +++ /dev/null @@ -1,8 +0,0 @@ - Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: max-age=10800, private, must-revalidate - [2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php deleted file mode 100644 index a0f635c..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php +++ /dev/null @@ -1,10 +0,0 @@ - bar -) -$_SESSION is not empty -write -destroy -close -$_SESSION is not empty -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: max-age=0, private, must-revalidate -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php deleted file mode 100644 index 96dca3c..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php +++ /dev/null @@ -1,24 +0,0 @@ -setSaveHandler(new TestSessionHandler()); -$flash = new FlashBag(); -$storage->registerBag($flash); -$storage->start(); - -$flash->add('foo', 'bar'); - -print_r($flash->get('foo')); -echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty'; -echo "\n"; - -$storage->save(); - -echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty'; - -ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); }); diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected deleted file mode 100644 index 33da0a5..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected +++ /dev/null @@ -1,15 +0,0 @@ -open -validateId -read -doRead: abc|i:123; -read - -updateTimestamp -close -Array -( - [0] => Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: max-age=10800, private, must-revalidate - [2] => Set-Cookie: abc=def -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php deleted file mode 100644 index ffb5b20..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php +++ /dev/null @@ -1,8 +0,0 @@ - Content-Type: text/plain; charset=utf-8 - [1] => Cache-Control: max-age=10800, private, must-revalidate - [2] => Set-Cookie: abc=def -) -shutdown diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php deleted file mode 100644 index ec51193..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php +++ /dev/null @@ -1,13 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler; - -/** - * @requires extension memcache - * @group time-sensitive - * @group legacy - */ -class MemcacheSessionHandlerTest extends TestCase -{ - const PREFIX = 'prefix_'; - const TTL = 1000; - - /** - * @var MemcacheSessionHandler - */ - protected $storage; - - protected $memcache; - - protected function setUp() - { - if (\defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - - parent::setUp(); - $this->memcache = $this->getMockBuilder('Memcache')->getMock(); - $this->storage = new MemcacheSessionHandler( - $this->memcache, - array('prefix' => self::PREFIX, 'expiretime' => self::TTL) - ); - } - - protected function tearDown() - { - $this->memcache = null; - $this->storage = null; - parent::tearDown(); - } - - public function testOpenSession() - { - $this->assertTrue($this->storage->open('', '')); - } - - public function testCloseSession() - { - $this->assertTrue($this->storage->close()); - } - - public function testReadSession() - { - $this->memcache - ->expects($this->once()) - ->method('get') - ->with(self::PREFIX.'id') - ; - - $this->assertEquals('', $this->storage->read('id')); - } - - public function testWriteSession() - { - $this->memcache - ->expects($this->once()) - ->method('set') - ->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2)) - ->will($this->returnValue(true)) - ; - - $this->assertTrue($this->storage->write('id', 'data')); - } - - public function testDestroySession() - { - $this->memcache - ->expects($this->once()) - ->method('delete') - ->with(self::PREFIX.'id') - ->will($this->returnValue(true)) - ; - - $this->assertTrue($this->storage->destroy('id')); - } - - public function testGcSession() - { - $this->assertTrue($this->storage->gc(123)); - } - - /** - * @dataProvider getOptionFixtures - */ - public function testSupportedOptions($options, $supported) - { - try { - new MemcacheSessionHandler($this->memcache, $options); - $this->assertTrue($supported); - } catch (\InvalidArgumentException $e) { - $this->assertFalse($supported); - } - } - - public function getOptionFixtures() - { - return array( - array(array('prefix' => 'session'), true), - array(array('expiretime' => 100), true), - array(array('prefix' => 'session', 'expiretime' => 200), true), - array(array('expiretime' => 100, 'foo' => 'bar'), false), - ); - } - - public function testGetConnection() - { - $method = new \ReflectionMethod($this->storage, 'getMemcache'); - $method->setAccessible(true); - - $this->assertInstanceOf('\Memcache', $method->invoke($this->storage)); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php deleted file mode 100644 index 2a11480..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler; - -/** - * @requires extension memcached - * @group time-sensitive - */ -class MemcachedSessionHandlerTest extends TestCase -{ - const PREFIX = 'prefix_'; - const TTL = 1000; - - /** - * @var MemcachedSessionHandler - */ - protected $storage; - - protected $memcached; - - protected function setUp() - { - if (\defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - - parent::setUp(); - - if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) { - $this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower, or 3.0.0b1 or higher'); - } - - $this->memcached = $this->getMockBuilder('Memcached')->getMock(); - $this->storage = new MemcachedSessionHandler( - $this->memcached, - array('prefix' => self::PREFIX, 'expiretime' => self::TTL) - ); - } - - protected function tearDown() - { - $this->memcached = null; - $this->storage = null; - parent::tearDown(); - } - - public function testOpenSession() - { - $this->assertTrue($this->storage->open('', '')); - } - - public function testCloseSession() - { - $this->assertTrue($this->storage->close()); - } - - public function testReadSession() - { - $this->memcached - ->expects($this->once()) - ->method('get') - ->with(self::PREFIX.'id') - ; - - $this->assertEquals('', $this->storage->read('id')); - } - - public function testWriteSession() - { - $this->memcached - ->expects($this->once()) - ->method('set') - ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2)) - ->will($this->returnValue(true)) - ; - - $this->assertTrue($this->storage->write('id', 'data')); - } - - public function testDestroySession() - { - $this->memcached - ->expects($this->once()) - ->method('delete') - ->with(self::PREFIX.'id') - ->will($this->returnValue(true)) - ; - - $this->assertTrue($this->storage->destroy('id')); - } - - public function testGcSession() - { - $this->assertTrue($this->storage->gc(123)); - } - - /** - * @dataProvider getOptionFixtures - */ - public function testSupportedOptions($options, $supported) - { - try { - new MemcachedSessionHandler($this->memcached, $options); - $this->assertTrue($supported); - } catch (\InvalidArgumentException $e) { - $this->assertFalse($supported); - } - } - - public function getOptionFixtures() - { - return array( - array(array('prefix' => 'session'), true), - array(array('expiretime' => 100), true), - array(array('prefix' => 'session', 'expiretime' => 200), true), - array(array('expiretime' => 100, 'foo' => 'bar'), false), - ); - } - - public function testGetConnection() - { - $method = new \ReflectionMethod($this->storage, 'getMemcached'); - $method->setAccessible(true); - - $this->assertInstanceOf('\Memcached', $method->invoke($this->storage)); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php deleted file mode 100644 index 186fc0a..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ /dev/null @@ -1,333 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - -/** - * @author Markus Bachmann - * @group time-sensitive - * @group legacy - */ -class MongoDbSessionHandlerTest extends TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $mongo; - private $storage; - public $options; - - protected function setUp() - { - parent::setUp(); - - if (\extension_loaded('mongodb')) { - if (!class_exists('MongoDB\Client')) { - $this->markTestSkipped('The mongodb/mongodb package is required.'); - } - } elseif (!\extension_loaded('mongo')) { - $this->markTestSkipped('The Mongo or MongoDB extension is required.'); - } - - if (phpversion('mongodb')) { - $mongoClass = 'MongoDB\Client'; - } else { - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; - } - - $this->mongo = $this->getMockBuilder($mongoClass) - ->disableOriginalConstructor() - ->getMock(); - - $this->options = array( - 'id_field' => '_id', - 'data_field' => 'data', - 'time_field' => 'time', - 'expiry_field' => 'expires_at', - 'database' => 'sf2-test', - 'collection' => 'session-test', - ); - - $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructorShouldThrowExceptionForInvalidMongo() - { - new MongoDbSessionHandler(new \stdClass(), $this->options); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructorShouldThrowExceptionForMissingOptions() - { - new MongoDbSessionHandler($this->mongo, array()); - } - - public function testOpenMethodAlwaysReturnTrue() - { - $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true'); - } - - public function testCloseMethodAlwaysReturnTrue() - { - $this->assertTrue($this->storage->close(), 'The "close" method should always return true'); - } - - public function testRead() - { - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - // defining the timeout before the actual method call - // allows to test for "greater than" values in the $criteria - $testTimeout = time() + 1; - - $collection->expects($this->once()) - ->method('findOne') - ->will($this->returnCallback(function ($criteria) use ($testTimeout) { - $this->assertArrayHasKey($this->options['id_field'], $criteria); - $this->assertEquals($criteria[$this->options['id_field']], 'foo'); - - $this->assertArrayHasKey($this->options['expiry_field'], $criteria); - $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]); - - if (phpversion('mongodb')) { - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout); - } else { - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); - } - - $fields = array( - $this->options['id_field'] => 'foo', - ); - - if (phpversion('mongodb')) { - $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY); - $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000); - } else { - $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY); - $fields[$this->options['id_field']] = new \MongoDate(); - } - - return $fields; - })); - - $this->assertEquals('bar', $this->storage->read('foo')); - } - - public function testWrite() - { - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - $data = array(); - - $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; - - $collection->expects($this->once()) - ->method($methodName) - ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { - $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - - if (phpversion('mongodb')) { - $this->assertEquals(array('upsert' => true), $options); - } else { - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); - } - - $data = $updateData['$set']; - })); - - $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); - $this->assertTrue($this->storage->write('foo', 'bar')); - - if (phpversion('mongodb')) { - $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000)); - } else { - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); - } - } - - public function testWriteWhenUsingExpiresField() - { - $this->options = array( - 'id_field' => '_id', - 'data_field' => 'data', - 'time_field' => 'time', - 'database' => 'sf2-test', - 'collection' => 'session-test', - 'expiry_field' => 'expiresAt', - ); - - $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); - - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - $data = array(); - - $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; - - $collection->expects($this->once()) - ->method($methodName) - ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { - $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - - if (phpversion('mongodb')) { - $this->assertEquals(array('upsert' => true), $options); - } else { - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); - } - - $data = $updateData['$set']; - })); - - $this->assertTrue($this->storage->write('foo', 'bar')); - - if (phpversion('mongodb')) { - $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); - } else { - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); - } - } - - public function testReplaceSessionData() - { - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - $data = array(); - - $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; - - $collection->expects($this->exactly(2)) - ->method($methodName) - ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { - $data = $updateData; - })); - - $this->storage->write('foo', 'bar'); - $this->storage->write('foo', 'foobar'); - - if (phpversion('mongodb')) { - $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData()); - } else { - $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin); - } - } - - public function testDestroy() - { - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove'; - - $collection->expects($this->once()) - ->method($methodName) - ->with(array($this->options['id_field'] => 'foo')); - - $this->assertTrue($this->storage->destroy('foo')); - } - - public function testGc() - { - $collection = $this->createMongoCollectionMock(); - - $this->mongo->expects($this->once()) - ->method('selectCollection') - ->with($this->options['database'], $this->options['collection']) - ->will($this->returnValue($collection)); - - $methodName = phpversion('mongodb') ? 'deleteMany' : 'remove'; - - $collection->expects($this->once()) - ->method($methodName) - ->will($this->returnCallback(function ($criteria) { - if (phpversion('mongodb')) { - $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000)); - } else { - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); - } - })); - - $this->assertTrue($this->storage->gc(1)); - } - - public function testGetConnection() - { - $method = new \ReflectionMethod($this->storage, 'getMongo'); - $method->setAccessible(true); - - if (phpversion('mongodb')) { - $mongoClass = 'MongoDB\Client'; - } else { - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; - } - - $this->assertInstanceOf($mongoClass, $method->invoke($this->storage)); - } - - private function createMongoCollectionMock() - { - $collectionClass = 'MongoCollection'; - if (phpversion('mongodb')) { - $collectionClass = 'MongoDB\Collection'; - } - - $collection = $this->getMockBuilder($collectionClass) - ->disableOriginalConstructor() - ->getMock(); - - return $collection; - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php deleted file mode 100644 index a6264e5..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; -use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; - -/** - * Test class for NativeFileSessionHandler. - * - * @author Drak - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ -class NativeFileSessionHandlerTest extends TestCase -{ - public function testConstruct() - { - $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir())); - - $this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName()); - $this->assertEquals('user', ini_get('session.save_handler')); - - $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path')); - $this->assertEquals('TESTING', ini_get('session.name')); - } - - /** - * @dataProvider savePathDataProvider - */ - public function testConstructSavePath($savePath, $expectedSavePath, $path) - { - $handler = new NativeFileSessionHandler($savePath); - $this->assertEquals($expectedSavePath, ini_get('session.save_path')); - $this->assertTrue(is_dir(realpath($path))); - - rmdir($path); - } - - public function savePathDataProvider() - { - $base = sys_get_temp_dir(); - - return array( - array("$base/foo", "$base/foo", "$base/foo"), - array("5;$base/foo", "5;$base/foo", "$base/foo"), - array("5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"), - ); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructException() - { - $handler = new NativeFileSessionHandler('something;invalid;with;too-many-args'); - } - - public function testConstructDefault() - { - $path = ini_get('session.save_path'); - $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler()); - - $this->assertEquals($path, ini_get('session.save_path')); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php deleted file mode 100644 index 4a9fb60..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; - -/** - * Test class for NativeSessionHandler. - * - * @author Drak - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - * @group legacy - */ -class NativeSessionHandlerTest extends TestCase -{ - /** - * @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead. - */ - public function testConstruct() - { - $handler = new NativeSessionHandler(); - - $this->assertInstanceOf('SessionHandler', $handler); - $this->assertTrue($handler instanceof NativeSessionHandler); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php deleted file mode 100644 index 9a2212b..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; -use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; - -/** - * Test class for NullSessionHandler. - * - * @author Drak - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ -class NullSessionHandlerTest extends TestCase -{ - public function testSaveHandlers() - { - $storage = $this->getStorage(); - $this->assertEquals('user', ini_get('session.save_handler')); - } - - public function testSession() - { - session_id('nullsessionstorage'); - $storage = $this->getStorage(); - $session = new Session($storage); - $this->assertNull($session->get('something')); - $session->set('something', 'unique'); - $this->assertEquals('unique', $session->get('something')); - } - - public function testNothingIsPersisted() - { - session_id('nullsessionstorage'); - $storage = $this->getStorage(); - $session = new Session($storage); - $session->start(); - $this->assertEquals('nullsessionstorage', $session->getId()); - $this->assertNull($session->get('something')); - } - - public function getStorage() - { - return new NativeSessionStorage(array(), new NullSessionHandler()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php deleted file mode 100644 index 853e96d..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ /dev/null @@ -1,411 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - -/** - * @requires extension pdo_sqlite - * @group time-sensitive - */ -class PdoSessionHandlerTest extends TestCase -{ - private $dbFile; - - protected function tearDown() - { - // make sure the temporary database file is deleted when it has been created (even when a test fails) - if ($this->dbFile) { - @unlink($this->dbFile); - } - parent::tearDown(); - } - - protected function getPersistentSqliteDsn() - { - $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); - - return 'sqlite:'.$this->dbFile; - } - - protected function getMemorySqlitePdo() - { - $pdo = new \PDO('sqlite::memory:'); - $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $storage = new PdoSessionHandler($pdo); - $storage->createTable(); - - return $pdo; - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testWrongPdoErrMode() - { - $pdo = $this->getMemorySqlitePdo(); - $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - - $storage = new PdoSessionHandler($pdo); - } - - /** - * @expectedException \RuntimeException - */ - public function testInexistentTable() - { - $storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table')); - $storage->open('', 'sid'); - $storage->read('id'); - $storage->write('id', 'data'); - $storage->close(); - } - - /** - * @expectedException \RuntimeException - */ - public function testCreateTableTwice() - { - $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); - $storage->createTable(); - } - - public function testWithLazyDsnConnection() - { - $dsn = $this->getPersistentSqliteDsn(); - - $storage = new PdoSessionHandler($dsn); - $storage->createTable(); - $storage->open('', 'sid'); - $data = $storage->read('id'); - $storage->write('id', 'data'); - $storage->close(); - $this->assertSame('', $data, 'New session returns empty string data'); - - $storage->open('', 'sid'); - $data = $storage->read('id'); - $storage->close(); - $this->assertSame('data', $data, 'Written value can be read back correctly'); - } - - public function testWithLazySavePathConnection() - { - $dsn = $this->getPersistentSqliteDsn(); - - // Open is called with what ini_set('session.save_path', $dsn) would mean - $storage = new PdoSessionHandler(null); - $storage->open($dsn, 'sid'); - $storage->createTable(); - $data = $storage->read('id'); - $storage->write('id', 'data'); - $storage->close(); - $this->assertSame('', $data, 'New session returns empty string data'); - - $storage->open($dsn, 'sid'); - $data = $storage->read('id'); - $storage->close(); - $this->assertSame('data', $data, 'Written value can be read back correctly'); - } - - public function testReadWriteReadWithNullByte() - { - $sessionData = 'da'."\0".'ta'; - - $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); - $storage->open('', 'sid'); - $readData = $storage->read('id'); - $storage->write('id', $sessionData); - $storage->close(); - $this->assertSame('', $readData, 'New session returns empty string data'); - - $storage->open('', 'sid'); - $readData = $storage->read('id'); - $storage->close(); - $this->assertSame($sessionData, $readData, 'Written value can be read back correctly'); - } - - public function testReadConvertsStreamToString() - { - if (\defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - - $pdo = new MockPdo('pgsql'); - $pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock(); - - $content = 'foobar'; - $stream = $this->createStream($content); - - $pdo->prepareResult->expects($this->once())->method('fetchAll') - ->will($this->returnValue(array(array($stream, 42, time())))); - - $storage = new PdoSessionHandler($pdo); - $result = $storage->read('foo'); - - $this->assertSame($content, $result); - } - - public function testReadLockedConvertsStreamToString() - { - if (\defined('HHVM_VERSION')) { - $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289'); - } - if (filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN)) { - $this->markTestSkipped('Strict mode needs no locking for new sessions.'); - } - - $pdo = new MockPdo('pgsql'); - $selectStmt = $this->getMockBuilder('PDOStatement')->getMock(); - $insertStmt = $this->getMockBuilder('PDOStatement')->getMock(); - - $pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) { - return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt; - }; - - $content = 'foobar'; - $stream = $this->createStream($content); - $exception = null; - - $selectStmt->expects($this->atLeast(2))->method('fetchAll') - ->will($this->returnCallback(function () use (&$exception, $stream) { - return $exception ? array(array($stream, 42, time())) : array(); - })); - - $insertStmt->expects($this->once())->method('execute') - ->will($this->returnCallback(function () use (&$exception) { - throw $exception = new \PDOException('', '23'); - })); - - $storage = new PdoSessionHandler($pdo); - $result = $storage->read('foo'); - - $this->assertSame($content, $result); - } - - public function testReadingRequiresExactlySameId() - { - $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); - $storage->open('', 'sid'); - $storage->write('id', 'data'); - $storage->write('test', 'data'); - $storage->write('space ', 'data'); - $storage->close(); - - $storage->open('', 'sid'); - $readDataCaseSensitive = $storage->read('ID'); - $readDataNoCharFolding = $storage->read('tést'); - $readDataKeepSpace = $storage->read('space '); - $readDataExtraSpace = $storage->read('space '); - $storage->close(); - - $this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)'); - $this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)'); - $this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is'); - $this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is'); - } - - /** - * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace). - */ - public function testWriteDifferentSessionIdThanRead() - { - $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); - $storage->open('', 'sid'); - $storage->read('id'); - $storage->destroy('id'); - $storage->write('new_id', 'data_of_new_session_id'); - $storage->close(); - - $storage->open('', 'sid'); - $data = $storage->read('new_id'); - $storage->close(); - - $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available'); - } - - public function testWrongUsageStillWorks() - { - // wrong method sequence that should no happen, but still works - $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); - $storage->write('id', 'data'); - $storage->write('other_id', 'other_data'); - $storage->destroy('inexistent'); - $storage->open('', 'sid'); - $data = $storage->read('id'); - $otherData = $storage->read('other_id'); - $storage->close(); - - $this->assertSame('data', $data); - $this->assertSame('other_data', $otherData); - } - - public function testSessionDestroy() - { - $pdo = $this->getMemorySqlitePdo(); - $storage = new PdoSessionHandler($pdo); - - $storage->open('', 'sid'); - $storage->read('id'); - $storage->write('id', 'data'); - $storage->close(); - $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); - - $storage->open('', 'sid'); - $storage->read('id'); - $storage->destroy('id'); - $storage->close(); - $this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); - - $storage->open('', 'sid'); - $data = $storage->read('id'); - $storage->close(); - $this->assertSame('', $data, 'Destroyed session returns empty string'); - } - - /** - * @runInSeparateProcess - */ - public function testSessionGC() - { - $previousLifeTime = ini_set('session.gc_maxlifetime', 1000); - $pdo = $this->getMemorySqlitePdo(); - $storage = new PdoSessionHandler($pdo); - - $storage->open('', 'sid'); - $storage->read('id'); - $storage->write('id', 'data'); - $storage->close(); - - $storage->open('', 'sid'); - $storage->read('gc_id'); - ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read - $storage->write('gc_id', 'data'); - $storage->close(); - $this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called'); - - $storage->open('', 'sid'); - $data = $storage->read('gc_id'); - $storage->gc(-1); - $storage->close(); - - ini_set('session.gc_maxlifetime', $previousLifeTime); - - $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet'); - $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned'); - } - - public function testGetConnection() - { - $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); - - $method = new \ReflectionMethod($storage, 'getConnection'); - $method->setAccessible(true); - - $this->assertInstanceOf('\PDO', $method->invoke($storage)); - } - - public function testGetConnectionConnectsIfNeeded() - { - $storage = new PdoSessionHandler('sqlite::memory:'); - - $method = new \ReflectionMethod($storage, 'getConnection'); - $method->setAccessible(true); - - $this->assertInstanceOf('\PDO', $method->invoke($storage)); - } - - /** - * @dataProvider provideUrlDsnPairs - */ - public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null) - { - $storage = new PdoSessionHandler($url); - - $this->assertAttributeEquals($expectedDsn, 'dsn', $storage); - - if (null !== $expectedUser) { - $this->assertAttributeEquals($expectedUser, 'username', $storage); - } - - if (null !== $expectedPassword) { - $this->assertAttributeEquals($expectedPassword, 'password', $storage); - } - } - - public function provideUrlDsnPairs() - { - yield array('mysql://localhost/test', 'mysql:host=localhost;dbname=test;'); - yield array('mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;'); - yield array('mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd'); - yield array('postgres://localhost/test', 'pgsql:host=localhost;dbname=test;'); - yield array('postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;'); - yield array('postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd'); - yield 'sqlite relative path' => array('sqlite://localhost/tmp/test', 'sqlite:tmp/test'); - yield 'sqlite absolute path' => array('sqlite://localhost//tmp/test', 'sqlite:/tmp/test'); - yield 'sqlite relative path without host' => array('sqlite:///tmp/test', 'sqlite:tmp/test'); - yield 'sqlite absolute path without host' => array('sqlite3:////tmp/test', 'sqlite:/tmp/test'); - yield array('sqlite://localhost/:memory:', 'sqlite::memory:'); - yield array('mssql://localhost/test', 'sqlsrv:server=localhost;Database=test'); - yield array('mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test'); - } - - private function createStream($content) - { - $stream = tmpfile(); - fwrite($stream, $content); - fseek($stream, 0); - - return $stream; - } -} - -class MockPdo extends \PDO -{ - public $prepareResult; - private $driverName; - private $errorMode; - - public function __construct($driverName = null, $errorMode = null) - { - $this->driverName = $driverName; - $this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION; - } - - public function getAttribute($attribute) - { - if (\PDO::ATTR_ERRMODE === $attribute) { - return $this->errorMode; - } - - if (\PDO::ATTR_DRIVER_NAME === $attribute) { - return $this->driverName; - } - - return parent::getAttribute($attribute); - } - - public function prepare($statement, $driverOptions = array()) - { - return \is_callable($this->prepareResult) - ? \call_user_func($this->prepareResult, $statement, $driverOptions) - : $this->prepareResult; - } - - public function beginTransaction() - { - } - - public function rollBack() - { - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php deleted file mode 100644 index b02c41a..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php +++ /dev/null @@ -1,189 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler; - -class StrictSessionHandlerTest extends TestCase -{ - public function testOpen() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('open') - ->with('path', 'name')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertInstanceOf('SessionUpdateTimestampHandlerInterface', $proxy); - $this->assertInstanceOf(AbstractSessionHandler::class, $proxy); - $this->assertTrue($proxy->open('path', 'name')); - } - - public function testCloseSession() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('close') - ->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->close()); - } - - public function testValidateIdOK() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn('data'); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->validateId('id')); - } - - public function testValidateIdKO() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn(''); - $proxy = new StrictSessionHandler($handler); - - $this->assertFalse($proxy->validateId('id')); - } - - public function testRead() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn('data'); - $proxy = new StrictSessionHandler($handler); - - $this->assertSame('data', $proxy->read('id')); - } - - public function testReadWithValidateIdOK() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn('data'); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->validateId('id')); - $this->assertSame('data', $proxy->read('id')); - } - - public function testReadWithValidateIdMismatch() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->exactly(2))->method('read') - ->withConsecutive(array('id1'), array('id2')) - ->will($this->onConsecutiveCalls('data1', 'data2')); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->validateId('id1')); - $this->assertSame('data2', $proxy->read('id2')); - } - - public function testUpdateTimestamp() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('write') - ->with('id', 'data')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->updateTimestamp('id', 'data')); - } - - public function testWrite() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('write') - ->with('id', 'data')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->write('id', 'data')); - } - - public function testWriteEmptyNewSession() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn(''); - $handler->expects($this->never())->method('write'); - $handler->expects($this->once())->method('destroy')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertFalse($proxy->validateId('id')); - $this->assertSame('', $proxy->read('id')); - $this->assertTrue($proxy->write('id', '')); - } - - public function testWriteEmptyExistingSession() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn('data'); - $handler->expects($this->never())->method('write'); - $handler->expects($this->once())->method('destroy')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertSame('data', $proxy->read('id')); - $this->assertTrue($proxy->write('id', '')); - } - - public function testDestroy() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('destroy') - ->with('id')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->destroy('id')); - } - - public function testDestroyNewSession() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn(''); - $handler->expects($this->once())->method('destroy')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertSame('', $proxy->read('id')); - $this->assertTrue($proxy->destroy('id')); - } - - public function testDestroyNonEmptyNewSession() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('read') - ->with('id')->willReturn(''); - $handler->expects($this->once())->method('write') - ->with('id', 'data')->willReturn(true); - $handler->expects($this->once())->method('destroy') - ->with('id')->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertSame('', $proxy->read('id')); - $this->assertTrue($proxy->write('id', 'data')); - $this->assertTrue($proxy->destroy('id')); - } - - public function testGc() - { - $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $handler->expects($this->once())->method('gc') - ->with(123)->willReturn(true); - $proxy = new StrictSessionHandler($handler); - - $this->assertTrue($proxy->gc(123)); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php deleted file mode 100644 index 898a7d1..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler; - -/** - * @author Adrien Brault - * - * @group legacy - */ -class WriteCheckSessionHandlerTest extends TestCase -{ - public function test() - { - $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); - - $wrappedSessionHandlerMock - ->expects($this->once()) - ->method('close') - ->with() - ->will($this->returnValue(true)) - ; - - $this->assertTrue($writeCheckSessionHandler->close()); - } - - public function testWrite() - { - $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); - - $wrappedSessionHandlerMock - ->expects($this->once()) - ->method('write') - ->with('foo', 'bar') - ->will($this->returnValue(true)) - ; - - $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar')); - } - - public function testSkippedWrite() - { - $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); - - $wrappedSessionHandlerMock - ->expects($this->once()) - ->method('read') - ->with('foo') - ->will($this->returnValue('bar')) - ; - - $wrappedSessionHandlerMock - ->expects($this->never()) - ->method('write') - ; - - $this->assertEquals('bar', $writeCheckSessionHandler->read('foo')); - $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar')); - } - - public function testNonSkippedWrite() - { - $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); - - $wrappedSessionHandlerMock - ->expects($this->once()) - ->method('read') - ->with('foo') - ->will($this->returnValue('bar')) - ; - - $wrappedSessionHandlerMock - ->expects($this->once()) - ->method('write') - ->with('foo', 'baZZZ') - ->will($this->returnValue(true)) - ; - - $this->assertEquals('bar', $writeCheckSessionHandler->read('foo')); - $this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ')); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php deleted file mode 100644 index 69cf616..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; - -/** - * Test class for MetadataBag. - * - * @group time-sensitive - */ -class MetadataBagTest extends TestCase -{ - /** - * @var MetadataBag - */ - protected $bag; - - protected $array = array(); - - protected function setUp() - { - parent::setUp(); - $this->bag = new MetadataBag(); - $this->array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0); - $this->bag->initialize($this->array); - } - - protected function tearDown() - { - $this->array = array(); - $this->bag = null; - parent::tearDown(); - } - - public function testInitialize() - { - $sessionMetadata = array(); - - $bag1 = new MetadataBag(); - $bag1->initialize($sessionMetadata); - $this->assertGreaterThanOrEqual(time(), $bag1->getCreated()); - $this->assertEquals($bag1->getCreated(), $bag1->getLastUsed()); - - sleep(1); - $bag2 = new MetadataBag(); - $bag2->initialize($sessionMetadata); - $this->assertEquals($bag1->getCreated(), $bag2->getCreated()); - $this->assertEquals($bag1->getLastUsed(), $bag2->getLastUsed()); - $this->assertEquals($bag2->getCreated(), $bag2->getLastUsed()); - - sleep(1); - $bag3 = new MetadataBag(); - $bag3->initialize($sessionMetadata); - $this->assertEquals($bag1->getCreated(), $bag3->getCreated()); - $this->assertGreaterThan($bag2->getLastUsed(), $bag3->getLastUsed()); - $this->assertNotEquals($bag3->getCreated(), $bag3->getLastUsed()); - } - - public function testGetSetName() - { - $this->assertEquals('__metadata', $this->bag->getName()); - $this->bag->setName('foo'); - $this->assertEquals('foo', $this->bag->getName()); - } - - public function testGetStorageKey() - { - $this->assertEquals('_sf2_meta', $this->bag->getStorageKey()); - } - - public function testGetLifetime() - { - $bag = new MetadataBag(); - $array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000); - $bag->initialize($array); - $this->assertEquals(1000, $bag->getLifetime()); - } - - public function testGetCreated() - { - $this->assertEquals(1234567, $this->bag->getCreated()); - } - - public function testGetLastUsed() - { - $this->assertLessThanOrEqual(time(), $this->bag->getLastUsed()); - } - - public function testClear() - { - $this->bag->clear(); - - // the clear method has no side effects, we just want to ensure it doesn't trigger any exceptions - $this->addToAssertionCount(1); - } - - public function testSkipLastUsedUpdate() - { - $bag = new MetadataBag('', 30); - $timeStamp = time(); - - $created = $timeStamp - 15; - $sessionMetadata = array( - MetadataBag::CREATED => $created, - MetadataBag::UPDATED => $created, - MetadataBag::LIFETIME => 1000, - ); - $bag->initialize($sessionMetadata); - - $this->assertEquals($created, $sessionMetadata[MetadataBag::UPDATED]); - } - - public function testDoesNotSkipLastUsedUpdate() - { - $bag = new MetadataBag('', 30); - $timeStamp = time(); - - $created = $timeStamp - 45; - $sessionMetadata = array( - MetadataBag::CREATED => $created, - MetadataBag::UPDATED => $created, - MetadataBag::LIFETIME => 1000, - ); - $bag->initialize($sessionMetadata); - - $this->assertEquals($timeStamp, $sessionMetadata[MetadataBag::UPDATED]); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php deleted file mode 100644 index 893e120..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php +++ /dev/null @@ -1,131 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; - -/** - * Test class for MockArraySessionStorage. - * - * @author Drak - */ -class MockArraySessionStorageTest extends TestCase -{ - /** - * @var MockArraySessionStorage - */ - private $storage; - - /** - * @var AttributeBag - */ - private $attributes; - - /** - * @var FlashBag - */ - private $flashes; - - private $data; - - protected function setUp() - { - $this->attributes = new AttributeBag(); - $this->flashes = new FlashBag(); - - $this->data = array( - $this->attributes->getStorageKey() => array('foo' => 'bar'), - $this->flashes->getStorageKey() => array('notice' => 'hello'), - ); - - $this->storage = new MockArraySessionStorage(); - $this->storage->registerBag($this->flashes); - $this->storage->registerBag($this->attributes); - $this->storage->setSessionData($this->data); - } - - protected function tearDown() - { - $this->data = null; - $this->flashes = null; - $this->attributes = null; - $this->storage = null; - } - - public function testStart() - { - $this->assertEquals('', $this->storage->getId()); - $this->storage->start(); - $id = $this->storage->getId(); - $this->assertNotEquals('', $id); - $this->storage->start(); - $this->assertEquals($id, $this->storage->getId()); - } - - public function testRegenerate() - { - $this->storage->start(); - $id = $this->storage->getId(); - $this->storage->regenerate(); - $this->assertNotEquals($id, $this->storage->getId()); - $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all()); - $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll()); - - $id = $this->storage->getId(); - $this->storage->regenerate(true); - $this->assertNotEquals($id, $this->storage->getId()); - $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all()); - $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll()); - } - - public function testGetId() - { - $this->assertEquals('', $this->storage->getId()); - $this->storage->start(); - $this->assertNotEquals('', $this->storage->getId()); - } - - public function testClearClearsBags() - { - $this->storage->clear(); - - $this->assertSame(array(), $this->storage->getBag('attributes')->all()); - $this->assertSame(array(), $this->storage->getBag('flashes')->peekAll()); - } - - public function testClearStartsSession() - { - $this->storage->clear(); - - $this->assertTrue($this->storage->isStarted()); - } - - public function testClearWithNoBagsStartsSession() - { - $storage = new MockArraySessionStorage(); - - $storage->clear(); - - $this->assertTrue($storage->isStarted()); - } - - /** - * @expectedException \RuntimeException - */ - public function testUnstartedSave() - { - $this->storage->save(); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php deleted file mode 100644 index 1695798..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php +++ /dev/null @@ -1,127 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; - -/** - * Test class for MockFileSessionStorage. - * - * @author Drak - */ -class MockFileSessionStorageTest extends TestCase -{ - /** - * @var string - */ - private $sessionDir; - - /** - * @var MockFileSessionStorage - */ - protected $storage; - - protected function setUp() - { - $this->sessionDir = sys_get_temp_dir().'/sf2test'; - $this->storage = $this->getStorage(); - } - - protected function tearDown() - { - $this->sessionDir = null; - $this->storage = null; - array_map('unlink', glob($this->sessionDir.'/*.session')); - if (is_dir($this->sessionDir)) { - rmdir($this->sessionDir); - } - } - - public function testStart() - { - $this->assertEquals('', $this->storage->getId()); - $this->assertTrue($this->storage->start()); - $id = $this->storage->getId(); - $this->assertNotEquals('', $this->storage->getId()); - $this->assertTrue($this->storage->start()); - $this->assertEquals($id, $this->storage->getId()); - } - - public function testRegenerate() - { - $this->storage->start(); - $this->storage->getBag('attributes')->set('regenerate', 1234); - $this->storage->regenerate(); - $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate')); - $this->storage->regenerate(true); - $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate')); - } - - public function testGetId() - { - $this->assertEquals('', $this->storage->getId()); - $this->storage->start(); - $this->assertNotEquals('', $this->storage->getId()); - } - - public function testSave() - { - $this->storage->start(); - $id = $this->storage->getId(); - $this->assertNotEquals('108', $this->storage->getBag('attributes')->get('new')); - $this->assertFalse($this->storage->getBag('flashes')->has('newkey')); - $this->storage->getBag('attributes')->set('new', '108'); - $this->storage->getBag('flashes')->set('newkey', 'test'); - $this->storage->save(); - - $storage = $this->getStorage(); - $storage->setId($id); - $storage->start(); - $this->assertEquals('108', $storage->getBag('attributes')->get('new')); - $this->assertTrue($storage->getBag('flashes')->has('newkey')); - $this->assertEquals(array('test'), $storage->getBag('flashes')->peek('newkey')); - } - - public function testMultipleInstances() - { - $storage1 = $this->getStorage(); - $storage1->start(); - $storage1->getBag('attributes')->set('foo', 'bar'); - $storage1->save(); - - $storage2 = $this->getStorage(); - $storage2->setId($storage1->getId()); - $storage2->start(); - $this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances'); - } - - /** - * @expectedException \RuntimeException - */ - public function testSaveWithoutStart() - { - $storage1 = $this->getStorage(); - $storage1->save(); - } - - private function getStorage() - { - $storage = new MockFileSessionStorage($this->sessionDir); - $storage->registerBag(new FlashBag()); - $storage->registerBag(new AttributeBag()); - - return $storage; - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php deleted file mode 100644 index 52da294..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php +++ /dev/null @@ -1,294 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; -use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; -use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; -use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; - -/** - * Test class for NativeSessionStorage. - * - * @author Drak - * - * These tests require separate processes. - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ -class NativeSessionStorageTest extends TestCase -{ - private $savePath; - - protected function setUp() - { - $this->iniSet('session.save_handler', 'files'); - $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test'); - if (!is_dir($this->savePath)) { - mkdir($this->savePath); - } - } - - protected function tearDown() - { - session_write_close(); - array_map('unlink', glob($this->savePath.'/*')); - if (is_dir($this->savePath)) { - rmdir($this->savePath); - } - - $this->savePath = null; - } - - /** - * @return NativeSessionStorage - */ - protected function getStorage(array $options = array()) - { - $storage = new NativeSessionStorage($options); - $storage->registerBag(new AttributeBag()); - - return $storage; - } - - public function testBag() - { - $storage = $this->getStorage(); - $bag = new FlashBag(); - $storage->registerBag($bag); - $this->assertSame($bag, $storage->getBag($bag->getName())); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testRegisterBagException() - { - $storage = $this->getStorage(); - $storage->getBag('non_existing'); - } - - /** - * @expectedException \LogicException - */ - public function testRegisterBagForAStartedSessionThrowsException() - { - $storage = $this->getStorage(); - $storage->start(); - $storage->registerBag(new AttributeBag()); - } - - public function testGetId() - { - $storage = $this->getStorage(); - $this->assertSame('', $storage->getId(), 'Empty ID before starting session'); - - $storage->start(); - $id = $storage->getId(); - $this->assertInternalType('string', $id); - $this->assertNotSame('', $id); - - $storage->save(); - $this->assertSame($id, $storage->getId(), 'ID stays after saving session'); - } - - public function testRegenerate() - { - $storage = $this->getStorage(); - $storage->start(); - $id = $storage->getId(); - $storage->getBag('attributes')->set('lucky', 7); - $storage->regenerate(); - $this->assertNotEquals($id, $storage->getId()); - $this->assertEquals(7, $storage->getBag('attributes')->get('lucky')); - } - - public function testRegenerateDestroy() - { - $storage = $this->getStorage(); - $storage->start(); - $id = $storage->getId(); - $storage->getBag('attributes')->set('legs', 11); - $storage->regenerate(true); - $this->assertNotEquals($id, $storage->getId()); - $this->assertEquals(11, $storage->getBag('attributes')->get('legs')); - } - - public function testSessionGlobalIsUpToDateAfterIdRegeneration() - { - $storage = $this->getStorage(); - $storage->start(); - $storage->getBag('attributes')->set('lucky', 7); - $storage->regenerate(); - $storage->getBag('attributes')->set('lucky', 42); - - $this->assertEquals(42, $_SESSION['_sf2_attributes']['lucky']); - } - - public function testRegenerationFailureDoesNotFlagStorageAsStarted() - { - $storage = $this->getStorage(); - $this->assertFalse($storage->regenerate()); - $this->assertFalse($storage->isStarted()); - } - - public function testDefaultSessionCacheLimiter() - { - $this->iniSet('session.cache_limiter', 'nocache'); - - $storage = new NativeSessionStorage(); - $this->assertEquals('', ini_get('session.cache_limiter')); - } - - public function testExplicitSessionCacheLimiter() - { - $this->iniSet('session.cache_limiter', 'nocache'); - - $storage = new NativeSessionStorage(array('cache_limiter' => 'public')); - $this->assertEquals('public', ini_get('session.cache_limiter')); - } - - public function testCookieOptions() - { - $options = array( - 'cookie_lifetime' => 123456, - 'cookie_path' => '/my/cookie/path', - 'cookie_domain' => 'symfony.example.com', - 'cookie_secure' => true, - 'cookie_httponly' => false, - ); - - $this->getStorage($options); - $temp = session_get_cookie_params(); - $gco = array(); - - foreach ($temp as $key => $value) { - $gco['cookie_'.$key] = $value; - } - - $this->assertEquals($options, $gco); - } - - public function testSessionOptions() - { - if (\defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM is not handled in this test case.'); - } - - $options = array( - 'url_rewriter.tags' => 'a=href', - 'cache_expire' => '200', - ); - - $this->getStorage($options); - - $this->assertSame('a=href', ini_get('url_rewriter.tags')); - $this->assertSame('200', ini_get('session.cache_expire')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testSetSaveHandlerException() - { - $storage = $this->getStorage(); - $storage->setSaveHandler(new \stdClass()); - } - - public function testSetSaveHandler() - { - $this->iniSet('session.save_handler', 'files'); - $storage = $this->getStorage(); - $storage->setSaveHandler(); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); - $storage->setSaveHandler(null); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); - $storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler())); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); - $storage->setSaveHandler(new NativeFileSessionHandler()); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); - $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler())); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); - $storage->setSaveHandler(new NullSessionHandler()); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); - } - - /** - * @expectedException \RuntimeException - */ - public function testStarted() - { - $storage = $this->getStorage(); - - $this->assertFalse($storage->getSaveHandler()->isActive()); - $this->assertFalse($storage->isStarted()); - - session_start(); - $this->assertTrue(isset($_SESSION)); - $this->assertTrue($storage->getSaveHandler()->isActive()); - - // PHP session might have started, but the storage driver has not, so false is correct here - $this->assertFalse($storage->isStarted()); - - $key = $storage->getMetadataBag()->getStorageKey(); - $this->assertArrayNotHasKey($key, $_SESSION); - $storage->start(); - } - - public function testRestart() - { - $storage = $this->getStorage(); - $storage->start(); - $id = $storage->getId(); - $storage->getBag('attributes')->set('lucky', 7); - $storage->save(); - $storage->start(); - $this->assertSame($id, $storage->getId(), 'Same session ID after restarting'); - $this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available'); - } - - public function testCanCreateNativeSessionStorageWhenSessionAlreadyStarted() - { - session_start(); - $this->getStorage(); - - // Assert no exception has been thrown by `getStorage()` - $this->addToAssertionCount(1); - } - - public function testSetSessionOptionsOnceSessionStartedIsIgnored() - { - session_start(); - $this->getStorage(array( - 'name' => 'something-else', - )); - - // Assert no exception has been thrown by `getStorage()` - $this->addToAssertionCount(1); - } - - public function testGetBagsOnceSessionStartedIsIgnored() - { - session_start(); - $bag = new AttributeBag(); - $bag->setName('flashes'); - - $storage = $this->getStorage(); - $storage->registerBag($bag); - - $this->assertEquals($storage->getBag('flashes'), $bag); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php deleted file mode 100644 index 7cb63c5..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; -use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; - -/** - * Test class for PhpSessionStorage. - * - * @author Drak - * - * These tests require separate processes. - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ -class PhpBridgeSessionStorageTest extends TestCase -{ - private $savePath; - - protected function setUp() - { - $this->iniSet('session.save_handler', 'files'); - $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test'); - if (!is_dir($this->savePath)) { - mkdir($this->savePath); - } - } - - protected function tearDown() - { - session_write_close(); - array_map('unlink', glob($this->savePath.'/*')); - if (is_dir($this->savePath)) { - rmdir($this->savePath); - } - - $this->savePath = null; - } - - /** - * @return PhpBridgeSessionStorage - */ - protected function getStorage() - { - $storage = new PhpBridgeSessionStorage(); - $storage->registerBag(new AttributeBag()); - - return $storage; - } - - public function testPhpSession() - { - $storage = $this->getStorage(); - - $this->assertFalse($storage->getSaveHandler()->isActive()); - $this->assertFalse($storage->isStarted()); - - session_start(); - $this->assertTrue(isset($_SESSION)); - // in PHP 5.4 we can reliably detect a session started - $this->assertTrue($storage->getSaveHandler()->isActive()); - // PHP session might have started, but the storage driver has not, so false is correct here - $this->assertFalse($storage->isStarted()); - - $key = $storage->getMetadataBag()->getStorageKey(); - $this->assertArrayNotHasKey($key, $_SESSION); - $storage->start(); - $this->assertArrayHasKey($key, $_SESSION); - } - - public function testClear() - { - $storage = $this->getStorage(); - session_start(); - $_SESSION['drak'] = 'loves symfony'; - $storage->getBag('attributes')->set('symfony', 'greatness'); - $key = $storage->getBag('attributes')->getStorageKey(); - $this->assertEquals($_SESSION[$key], array('symfony' => 'greatness')); - $this->assertEquals($_SESSION['drak'], 'loves symfony'); - $storage->clear(); - $this->assertEquals($_SESSION[$key], array()); - $this->assertEquals($_SESSION['drak'], 'loves symfony'); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php deleted file mode 100644 index cbb291f..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; -use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; - -/** - * Test class for AbstractProxy. - * - * @author Drak - */ -class AbstractProxyTest extends TestCase -{ - /** - * @var AbstractProxy - */ - protected $proxy; - - protected function setUp() - { - $this->proxy = $this->getMockForAbstractClass(AbstractProxy::class); - } - - protected function tearDown() - { - $this->proxy = null; - } - - public function testGetSaveHandlerName() - { - $this->assertNull($this->proxy->getSaveHandlerName()); - } - - public function testIsSessionHandlerInterface() - { - $this->assertFalse($this->proxy->isSessionHandlerInterface()); - $sh = new SessionHandlerProxy(new \SessionHandler()); - $this->assertTrue($sh->isSessionHandlerInterface()); - } - - public function testIsWrapper() - { - $this->assertFalse($this->proxy->isWrapper()); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testIsActive() - { - $this->assertFalse($this->proxy->isActive()); - session_start(); - $this->assertTrue($this->proxy->isActive()); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testName() - { - $this->assertEquals(session_name(), $this->proxy->getName()); - $this->proxy->setName('foo'); - $this->assertEquals('foo', $this->proxy->getName()); - $this->assertEquals(session_name(), $this->proxy->getName()); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - * @expectedException \LogicException - */ - public function testNameException() - { - session_start(); - $this->proxy->setName('foo'); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testId() - { - $this->assertEquals(session_id(), $this->proxy->getId()); - $this->proxy->setId('foo'); - $this->assertEquals('foo', $this->proxy->getId()); - $this->assertEquals(session_id(), $this->proxy->getId()); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - * @expectedException \LogicException - */ - public function testIdException() - { - session_start(); - $this->proxy->setId('foo'); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php deleted file mode 100644 index ed4fee6..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; - -/** - * Test class for NativeProxy. - * - * @group legacy - * - * @author Drak - */ -class NativeProxyTest extends TestCase -{ - public function testIsWrapper() - { - $proxy = new NativeProxy(); - $this->assertFalse($proxy->isWrapper()); - } - - public function testGetSaveHandlerName() - { - $name = ini_get('session.save_handler'); - $proxy = new NativeProxy(); - $this->assertEquals($name, $proxy->getSaveHandlerName()); - } -} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php deleted file mode 100644 index 0b48250..0000000 --- a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php +++ /dev/null @@ -1,157 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; - -/** - * Tests for SessionHandlerProxy class. - * - * @author Drak - * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ -class SessionHandlerProxyTest extends TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_Matcher - */ - private $mock; - - /** - * @var SessionHandlerProxy - */ - private $proxy; - - protected function setUp() - { - $this->mock = $this->getMockBuilder('SessionHandlerInterface')->getMock(); - $this->proxy = new SessionHandlerProxy($this->mock); - } - - protected function tearDown() - { - $this->mock = null; - $this->proxy = null; - } - - public function testOpenTrue() - { - $this->mock->expects($this->once()) - ->method('open') - ->will($this->returnValue(true)); - - $this->assertFalse($this->proxy->isActive()); - $this->proxy->open('name', 'id'); - $this->assertFalse($this->proxy->isActive()); - } - - public function testOpenFalse() - { - $this->mock->expects($this->once()) - ->method('open') - ->will($this->returnValue(false)); - - $this->assertFalse($this->proxy->isActive()); - $this->proxy->open('name', 'id'); - $this->assertFalse($this->proxy->isActive()); - } - - public function testClose() - { - $this->mock->expects($this->once()) - ->method('close') - ->will($this->returnValue(true)); - - $this->assertFalse($this->proxy->isActive()); - $this->proxy->close(); - $this->assertFalse($this->proxy->isActive()); - } - - public function testCloseFalse() - { - $this->mock->expects($this->once()) - ->method('close') - ->will($this->returnValue(false)); - - $this->assertFalse($this->proxy->isActive()); - $this->proxy->close(); - $this->assertFalse($this->proxy->isActive()); - } - - public function testRead() - { - $this->mock->expects($this->once()) - ->method('read'); - - $this->proxy->read('id'); - } - - public function testWrite() - { - $this->mock->expects($this->once()) - ->method('write'); - - $this->proxy->write('id', 'data'); - } - - public function testDestroy() - { - $this->mock->expects($this->once()) - ->method('destroy'); - - $this->proxy->destroy('id'); - } - - public function testGc() - { - $this->mock->expects($this->once()) - ->method('gc'); - - $this->proxy->gc(86400); - } - - /** - * @requires PHPUnit 5.1 - */ - public function testValidateId() - { - $mock = $this->getMockBuilder(array('SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'))->getMock(); - $mock->expects($this->once()) - ->method('validateId'); - - $proxy = new SessionHandlerProxy($mock); - $proxy->validateId('id'); - - $this->assertTrue($this->proxy->validateId('id')); - } - - /** - * @requires PHPUnit 5.1 - */ - public function testUpdateTimestamp() - { - $mock = $this->getMockBuilder(array('SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'))->getMock(); - $mock->expects($this->once()) - ->method('updateTimestamp'); - - $proxy = new SessionHandlerProxy($mock); - $proxy->updateTimestamp('id', 'data'); - - $this->mock->expects($this->once()) - ->method('write'); - - $this->proxy->updateTimestamp('id', 'data'); - } -} diff --git a/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php b/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php deleted file mode 100644 index 699222e..0000000 --- a/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php +++ /dev/null @@ -1,144 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpFoundation\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\StreamedResponse; - -class StreamedResponseTest extends TestCase -{ - public function testConstructor() - { - $response = new StreamedResponse(function () { echo 'foo'; }, 404, array('Content-Type' => 'text/plain')); - - $this->assertEquals(404, $response->getStatusCode()); - $this->assertEquals('text/plain', $response->headers->get('Content-Type')); - } - - public function testPrepareWith11Protocol() - { - $response = new StreamedResponse(function () { echo 'foo'; }); - $request = Request::create('/'); - $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); - - $response->prepare($request); - - $this->assertEquals('1.1', $response->getProtocolVersion()); - $this->assertNotEquals('chunked', $response->headers->get('Transfer-Encoding'), 'Apache assumes responses with a Transfer-Encoding header set to chunked to already be encoded.'); - } - - public function testPrepareWith10Protocol() - { - $response = new StreamedResponse(function () { echo 'foo'; }); - $request = Request::create('/'); - $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); - - $response->prepare($request); - - $this->assertEquals('1.0', $response->getProtocolVersion()); - $this->assertNull($response->headers->get('Transfer-Encoding')); - } - - public function testPrepareWithHeadRequest() - { - $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Content-Length' => '123')); - $request = Request::create('/', 'HEAD'); - - $response->prepare($request); - - $this->assertSame('123', $response->headers->get('Content-Length')); - } - - public function testPrepareWithCacheHeaders() - { - $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Cache-Control' => 'max-age=600, public')); - $request = Request::create('/', 'GET'); - - $response->prepare($request); - $this->assertEquals('max-age=600, public', $response->headers->get('Cache-Control')); - } - - public function testSendContent() - { - $called = 0; - - $response = new StreamedResponse(function () use (&$called) { ++$called; }); - - $response->sendContent(); - $this->assertEquals(1, $called); - - $response->sendContent(); - $this->assertEquals(1, $called); - } - - /** - * @expectedException \LogicException - */ - public function testSendContentWithNonCallable() - { - $response = new StreamedResponse(null); - $response->sendContent(); - } - - /** - * @expectedException \LogicException - */ - public function testSetContent() - { - $response = new StreamedResponse(function () { echo 'foo'; }); - $response->setContent('foo'); - } - - public function testGetContent() - { - $response = new StreamedResponse(function () { echo 'foo'; }); - $this->assertFalse($response->getContent()); - } - - public function testCreate() - { - $response = StreamedResponse::create(function () {}, 204); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response); - $this->assertEquals(204, $response->getStatusCode()); - } - - public function testReturnThis() - { - $response = new StreamedResponse(function () {}); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent()); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent()); - - $response = new StreamedResponse(function () {}); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); - } - - public function testSetNotModified() - { - $response = new StreamedResponse(function () { echo 'foo'; }); - $modified = $response->setNotModified(); - $this->assertObjectHasAttribute('headers', $modified); - $this->assertObjectHasAttribute('content', $modified); - $this->assertObjectHasAttribute('version', $modified); - $this->assertObjectHasAttribute('statusCode', $modified); - $this->assertObjectHasAttribute('statusText', $modified); - $this->assertObjectHasAttribute('charset', $modified); - $this->assertEquals(304, $modified->getStatusCode()); - - ob_start(); - $modified->sendContent(); - $string = ob_get_clean(); - $this->assertEmpty($string); - } -} diff --git a/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng b/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng deleted file mode 100644 index 73708ca..0000000 --- a/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng b/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng deleted file mode 100644 index b9c3ca9..0000000 --- a/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uri - - - - rfc - - - (rfc|bcp|std)\d+ - - - - - rfc-errata - - - - draft - - - (draft|RFC)(-[a-zA-Z0-9]+)+ - - - - - registry - - - - person - - - - text - - - note - - - - unicode - - - ucd\d+\.\d+\.\d+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (\d+|0x[\da-fA-F]+)(\s*-\s*(\d+|0x[\da-fA-F]+))? - - - - - - - - - - - - - 0x[0-9]{8} - - - - - - [0-1]+ - - - - - - - - - - - - - - - - - - - - - - legacy - mib - template - json - - - - - - - - - - diff --git a/vendor/symfony/http-foundation/UrlHelper.php b/vendor/symfony/http-foundation/UrlHelper.php new file mode 100644 index 0000000..f114c0a --- /dev/null +++ b/vendor/symfony/http-foundation/UrlHelper.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\Routing\RequestContext; + +/** + * A helper service for manipulating URLs within and outside the request scope. + * + * @author Valentin Udaltsov + */ +final class UrlHelper +{ + private $requestStack; + private $requestContext; + + public function __construct(RequestStack $requestStack, RequestContext $requestContext = null) + { + $this->requestStack = $requestStack; + $this->requestContext = $requestContext; + } + + public function getAbsoluteUrl(string $path): string + { + if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) { + return $path; + } + + if (null === $request = $this->requestStack->getMasterRequest()) { + return $this->getAbsoluteUrlFromContext($path); + } + + if ('#' === $path[0]) { + $path = $request->getRequestUri().$path; + } elseif ('?' === $path[0]) { + $path = $request->getPathInfo().$path; + } + + if (!$path || '/' !== $path[0]) { + $prefix = $request->getPathInfo(); + $last = \strlen($prefix) - 1; + if ($last !== $pos = strrpos($prefix, '/')) { + $prefix = substr($prefix, 0, $pos).'/'; + } + + return $request->getUriForPath($prefix.$path); + } + + return $request->getSchemeAndHttpHost().$path; + } + + public function getRelativePath(string $path): string + { + if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) { + return $path; + } + + if (null === $request = $this->requestStack->getMasterRequest()) { + return $path; + } + + return $request->getRelativeUriForPath($path); + } + + private function getAbsoluteUrlFromContext(string $path): string + { + if (null === $this->requestContext || '' === $host = $this->requestContext->getHost()) { + return $path; + } + + $scheme = $this->requestContext->getScheme(); + $port = ''; + + if ('http' === $scheme && 80 !== $this->requestContext->getHttpPort()) { + $port = ':'.$this->requestContext->getHttpPort(); + } elseif ('https' === $scheme && 443 !== $this->requestContext->getHttpsPort()) { + $port = ':'.$this->requestContext->getHttpsPort(); + } + + if ('#' === $path[0]) { + $queryString = $this->requestContext->getQueryString(); + $path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path; + } elseif ('?' === $path[0]) { + $path = $this->requestContext->getPathInfo().$path; + } + + if ('/' !== $path[0]) { + $path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path; + } + + return $scheme.'://'.$host.$port.$path; + } +} diff --git a/vendor/symfony/http-foundation/composer.json b/vendor/symfony/http-foundation/composer.json index f6c6f2e..efc4b94 100644 --- a/vendor/symfony/http-foundation/composer.json +++ b/vendor/symfony/http-foundation/composer.json @@ -16,12 +16,13 @@ } ], "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php70": "~1.6" + "php": "^7.1.3", + "symfony/mime": "^4.3|^5.0", + "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" + "predis/predis": "~1.0", + "symfony/expression-language": "^3.4|^4.0|^5.0" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, @@ -32,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.4-dev" } } } diff --git a/vendor/symfony/http-foundation/phpunit.xml.dist b/vendor/symfony/http-foundation/phpunit.xml.dist deleted file mode 100644 index f57bc9e..0000000 --- a/vendor/symfony/http-foundation/phpunit.xml.dist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/vendor/symfony/mime/.gitattributes b/vendor/symfony/mime/.gitattributes new file mode 100644 index 0000000..ebb9287 --- /dev/null +++ b/vendor/symfony/mime/.gitattributes @@ -0,0 +1,3 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitignore export-ignore diff --git a/vendor/symfony/mime/Address.php b/vendor/symfony/mime/Address.php new file mode 100644 index 0000000..b0dcbd0 --- /dev/null +++ b/vendor/symfony/mime/Address.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Egulias\EmailValidator\EmailValidator; +use Egulias\EmailValidator\Validation\RFCValidation; +use Symfony\Component\Mime\Encoder\IdnAddressEncoder; +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Exception\LogicException; +use Symfony\Component\Mime\Exception\RfcComplianceException; + +/** + * @author Fabien Potencier + */ +final class Address +{ + /** + * A regex that matches a structure like 'Name '. + * It matches anything between the first < and last > as email address. + * This allows to use a single string to construct an Address, which can be convenient to use in + * config, and allows to have more readable config. + * This does not try to cover all edge cases for address. + */ + private const FROM_STRING_PATTERN = '~(?[^<]*)<(?.*)>[^>]*~'; + + private static $validator; + private static $encoder; + + private $address; + private $name; + + public function __construct(string $address, string $name = '') + { + if (!class_exists(EmailValidator::class)) { + throw new LogicException(sprintf('The "%s" class cannot be used as it needs "%s"; try running "composer require egulias/email-validator".', __CLASS__, EmailValidator::class)); + } + + if (null === self::$validator) { + self::$validator = new EmailValidator(); + } + + $this->address = trim($address); + $this->name = trim(str_replace(["\n", "\r"], '', $name)); + + if (!self::$validator->isValid($this->address, new RFCValidation())) { + throw new RfcComplianceException(sprintf('Email "%s" does not comply with addr-spec of RFC 2822.', $address)); + } + } + + public function getAddress(): string + { + return $this->address; + } + + public function getName(): string + { + return $this->name; + } + + public function getEncodedAddress(): string + { + if (null === self::$encoder) { + self::$encoder = new IdnAddressEncoder(); + } + + return self::$encoder->encodeString($this->address); + } + + public function toString(): string + { + return ($n = $this->getName()) ? $n.' <'.$this->getEncodedAddress().'>' : $this->getEncodedAddress(); + } + + /** + * @param Address|string $address + */ + public static function create($address): self + { + if ($address instanceof self) { + return $address; + } + if (\is_string($address)) { + return new self($address); + } + + throw new InvalidArgumentException(sprintf('An address can be an instance of Address or a string ("%s") given).', \is_object($address) ? \get_class($address) : \gettype($address))); + } + + /** + * @param (Address|string)[] $addresses + * + * @return Address[] + */ + public static function createArray(array $addresses): array + { + $addrs = []; + foreach ($addresses as $address) { + $addrs[] = self::create($address); + } + + return $addrs; + } + + public static function fromString(string $string): self + { + if (false === strpos($string, '<')) { + return new self($string, ''); + } + + if (!preg_match(self::FROM_STRING_PATTERN, $string, $matches)) { + throw new InvalidArgumentException(sprintf('Could not parse "%s" to a "%s" instance.', $string, static::class)); + } + + return new self($matches['addrSpec'], trim($matches['displayName'], ' \'"')); + } +} diff --git a/vendor/symfony/mime/BodyRendererInterface.php b/vendor/symfony/mime/BodyRendererInterface.php new file mode 100644 index 0000000..d692172 --- /dev/null +++ b/vendor/symfony/mime/BodyRendererInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +/** + * @author Fabien Potencier + */ +interface BodyRendererInterface +{ + public function render(Message $message): void; +} diff --git a/vendor/symfony/mime/CHANGELOG.md b/vendor/symfony/mime/CHANGELOG.md new file mode 100644 index 0000000..6148360 --- /dev/null +++ b/vendor/symfony/mime/CHANGELOG.md @@ -0,0 +1,20 @@ +CHANGELOG +========= + +4.4.0 +----- + + * [BC BREAK] Removed `NamedAddress` (`Address` now supports a name) + * Added PHPUnit constraints + * Added `AbstractPart::asDebugString()` + * Added `Address::fromString()` + +4.3.3 +----- + + * [BC BREAK] Renamed method `Headers::getAll()` to `Headers::all()`. + +4.3.0 +----- + + * Introduced the component as experimental diff --git a/vendor/symfony/mime/CharacterStream.php b/vendor/symfony/mime/CharacterStream.php new file mode 100644 index 0000000..749066f --- /dev/null +++ b/vendor/symfony/mime/CharacterStream.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +/** + * @author Fabien Potencier + * @author Xavier De Cock + * + * @internal + */ +final class CharacterStream +{ + /** Pre-computed for optimization */ + private const UTF8_LENGTH_MAP = [ + "\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1, + "\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1, + "\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1, + "\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1, + "\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, + "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, + "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, + "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, + "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, + "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, + "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, + "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, + "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, + "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, + "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, + "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1, + "\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0, + "\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0, + "\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0, + "\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0, + "\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0, + "\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0, + "\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0, + "\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0, + "\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2, + "\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2, + "\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2, + "\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2, + "\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3, + "\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3, + "\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4, + "\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0, + ]; + + private $data = ''; + private $dataSize = 0; + private $map = []; + private $charCount = 0; + private $currentPos = 0; + private $fixedWidth = 0; + + /** + * @param resource|string $input + */ + public function __construct($input, ?string $charset = 'utf-8') + { + $charset = strtolower(trim($charset)) ?: 'utf-8'; + if ('utf-8' === $charset || 'utf8' === $charset) { + $this->fixedWidth = 0; + $this->map = ['p' => [], 'i' => []]; + } else { + switch ($charset) { + // 16 bits + case 'ucs2': + case 'ucs-2': + case 'utf16': + case 'utf-16': + $this->fixedWidth = 2; + break; + + // 32 bits + case 'ucs4': + case 'ucs-4': + case 'utf32': + case 'utf-32': + $this->fixedWidth = 4; + break; + + // 7-8 bit charsets: (us-)?ascii, (iso|iec)-?8859-?[0-9]+, windows-?125[0-9], cp-?[0-9]+, ansi, macintosh, + // koi-?7, koi-?8-?.+, mik, (cork|t1), v?iscii + // and fallback + default: + $this->fixedWidth = 1; + } + } + if (\is_resource($input)) { + $blocks = 512; + if (stream_get_meta_data($input)['seekable'] ?? false) { + rewind($input); + } + while (false !== $read = fread($input, $blocks)) { + $this->write($read); + } + } else { + $this->write($input); + } + } + + public function read(int $length): ?string + { + if ($this->currentPos >= $this->charCount) { + return null; + } + $length = ($this->currentPos + $length > $this->charCount) ? $this->charCount - $this->currentPos : $length; + if ($this->fixedWidth > 0) { + $len = $length * $this->fixedWidth; + $ret = substr($this->data, $this->currentPos * $this->fixedWidth, $len); + $this->currentPos += $length; + } else { + $end = $this->currentPos + $length; + $end = $end > $this->charCount ? $this->charCount : $end; + $ret = ''; + $start = 0; + if ($this->currentPos > 0) { + $start = $this->map['p'][$this->currentPos - 1]; + } + $to = $start; + for (; $this->currentPos < $end; ++$this->currentPos) { + if (isset($this->map['i'][$this->currentPos])) { + $ret .= substr($this->data, $start, $to - $start).'?'; + $start = $this->map['p'][$this->currentPos]; + } else { + $to = $this->map['p'][$this->currentPos]; + } + } + $ret .= substr($this->data, $start, $to - $start); + } + + return $ret; + } + + public function readBytes(int $length): ?array + { + if (null !== $read = $this->read($length)) { + return array_map('ord', str_split($read, 1)); + } + + return null; + } + + public function setPointer(int $charOffset): void + { + if ($this->charCount < $charOffset) { + $charOffset = $this->charCount; + } + $this->currentPos = $charOffset; + } + + public function write(string $chars): void + { + $ignored = ''; + $this->data .= $chars; + if ($this->fixedWidth > 0) { + $strlen = \strlen($chars); + $ignoredL = $strlen % $this->fixedWidth; + $ignored = $ignoredL ? substr($chars, -$ignoredL) : ''; + $this->charCount += ($strlen - $ignoredL) / $this->fixedWidth; + } else { + $this->charCount += $this->getUtf8CharPositions($chars, $this->dataSize, $ignored); + } + $this->dataSize = \strlen($this->data) - \strlen($ignored); + } + + private function getUtf8CharPositions(string $string, int $startOffset, string &$ignoredChars): int + { + $strlen = \strlen($string); + $charPos = \count($this->map['p']); + $foundChars = 0; + $invalid = false; + for ($i = 0; $i < $strlen; ++$i) { + $char = $string[$i]; + $size = self::UTF8_LENGTH_MAP[$char]; + if (0 == $size) { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue; + } + + if ($invalid) { + /* We mark the chars as invalid and start a new char */ + $this->map['p'][$charPos + $foundChars] = $startOffset + $i; + $this->map['i'][$charPos + $foundChars] = true; + ++$foundChars; + $invalid = false; + } + if (($i + $size) > $strlen) { + $ignoredChars = substr($string, $i); + break; + } + for ($j = 1; $j < $size; ++$j) { + $char = $string[$i + $j]; + if ($char > "\x7F" && $char < "\xC0") { + // Valid - continue parsing + } else { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue 2; + } + } + /* Ok we got a complete char here */ + $this->map['p'][$charPos + $foundChars] = $startOffset + $i + $size; + $i += $j - 1; + ++$foundChars; + } + + return $foundChars; + } +} diff --git a/vendor/symfony/mime/Crypto/SMime.php b/vendor/symfony/mime/Crypto/SMime.php new file mode 100644 index 0000000..55941be --- /dev/null +++ b/vendor/symfony/mime/Crypto/SMime.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Crypto; + +use Symfony\Component\Mime\Exception\RuntimeException; +use Symfony\Component\Mime\Part\SMimePart; + +/** + * @author Sebastiaan Stok + * + * @internal + */ +abstract class SMime +{ + protected function normalizeFilePath(string $path): string + { + if (!file_exists($path)) { + throw new RuntimeException(sprintf('File does not exist: %s.', $path)); + } + + return 'file://'.str_replace('\\', '/', realpath($path)); + } + + protected function iteratorToFile(iterable $iterator, $stream): void + { + foreach ($iterator as $chunk) { + fwrite($stream, $chunk); + } + } + + protected function convertMessageToSMimePart($stream, string $type, string $subtype): SMimePart + { + rewind($stream); + + $headers = ''; + + while (!feof($stream)) { + $buffer = fread($stream, 78); + $headers .= $buffer; + + // Detect ending of header list + if (preg_match('/(\r\n\r\n|\n\n)/', $headers, $match)) { + $headersPosEnd = strpos($headers, $headerBodySeparator = $match[0]); + + break; + } + } + + $headers = $this->getMessageHeaders(trim(substr($headers, 0, $headersPosEnd))); + + fseek($stream, $headersPosEnd + \strlen($headerBodySeparator)); + + return new SMimePart($this->getStreamIterator($stream), $type, $subtype, $this->getParametersFromHeader($headers['content-type'])); + } + + protected function getStreamIterator($stream): iterable + { + while (!feof($stream)) { + yield fread($stream, 16372); + } + } + + private function getMessageHeaders(string $headerData): array + { + $headers = []; + $headerLines = explode("\r\n", str_replace("\n", "\r\n", str_replace("\r\n", "\n", $headerData))); + $currentHeaderName = ''; + + // Transform header lines into an associative array + foreach ($headerLines as $headerLine) { + // Empty lines between headers indicate a new mime-entity + if ('' === $headerLine) { + break; + } + + // Handle headers that span multiple lines + if (false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + return $headers; + } + + private function getParametersFromHeader(string $header): array + { + $params = []; + + preg_match_all('/(?P[a-z-0-9]+)=(?P"[^"]+"|(?:[^\s;]+|$))(?:\s+;)?/i', $header, $matches); + + foreach ($matches['value'] as $pos => $paramValue) { + $params[$matches['name'][$pos]] = trim($paramValue, '"'); + } + + return $params; + } +} diff --git a/vendor/symfony/mime/Crypto/SMimeEncrypter.php b/vendor/symfony/mime/Crypto/SMimeEncrypter.php new file mode 100644 index 0000000..d6961a6 --- /dev/null +++ b/vendor/symfony/mime/Crypto/SMimeEncrypter.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Crypto; + +use Symfony\Component\Mime\Exception\RuntimeException; +use Symfony\Component\Mime\Message; + +/** + * @author Sebastiaan Stok + */ +final class SMimeEncrypter extends SMime +{ + private $certs; + private $cipher; + + /** + * @param string|string[] $certificate The path (or array of paths) of the file(s) containing the X.509 certificate(s) + * @param int|null $cipher A set of algorithms used to encrypt the message. Must be one of these PHP constants: https://www.php.net/manual/en/openssl.ciphers.php + */ + public function __construct($certificate, int $cipher = null) + { + if (!\extension_loaded('openssl')) { + throw new \LogicException('PHP extension "openssl" is required to use SMime.'); + } + + if (\is_array($certificate)) { + $this->certs = array_map([$this, 'normalizeFilePath'], $certificate); + } else { + $this->certs = $this->normalizeFilePath($certificate); + } + + $this->cipher = $cipher ?? OPENSSL_CIPHER_AES_256_CBC; + } + + public function encrypt(Message $message): Message + { + $bufferFile = tmpfile(); + $outputFile = tmpfile(); + + $this->iteratorToFile($message->toIterable(), $bufferFile); + + if (!@openssl_pkcs7_encrypt(stream_get_meta_data($bufferFile)['uri'], stream_get_meta_data($outputFile)['uri'], $this->certs, [], 0, $this->cipher)) { + throw new RuntimeException(sprintf('Failed to encrypt S/Mime message. Error: "%s".', openssl_error_string())); + } + + $mimePart = $this->convertMessageToSMimePart($outputFile, 'application', 'pkcs7-mime'); + $mimePart->getHeaders() + ->addTextHeader('Content-Transfer-Encoding', 'base64') + ->addParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'smime.p7m']) + ; + + return new Message($message->getHeaders(), $mimePart); + } +} diff --git a/vendor/symfony/mime/Crypto/SMimeSigner.php b/vendor/symfony/mime/Crypto/SMimeSigner.php new file mode 100644 index 0000000..243aaf1 --- /dev/null +++ b/vendor/symfony/mime/Crypto/SMimeSigner.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Crypto; + +use Symfony\Component\Mime\Exception\RuntimeException; +use Symfony\Component\Mime\Message; + +/** + * @author Sebastiaan Stok + */ +final class SMimeSigner extends SMime +{ + private $signCertificate; + private $signPrivateKey; + private $signOptions; + private $extraCerts; + + /** + * @var string|null + */ + private $privateKeyPassphrase; + + /** + * @param string $certificate The path of the file containing the signing certificate (in PEM format) + * @param string $privateKey The path of the file containing the private key (in PEM format) + * @param string|null $privateKeyPassphrase A passphrase of the private key (if any) + * @param string|null $extraCerts The path of the file containing intermediate certificates (in PEM format) needed by the signing certificate + * @param int|null $signOptions Bitwise operator options for openssl_pkcs7_sign() (@see https://secure.php.net/manual/en/openssl.pkcs7.flags.php) + */ + public function __construct(string $certificate, string $privateKey, string $privateKeyPassphrase = null, string $extraCerts = null, int $signOptions = null) + { + if (!\extension_loaded('openssl')) { + throw new \LogicException('PHP extension "openssl" is required to use SMime.'); + } + + $this->signCertificate = $this->normalizeFilePath($certificate); + + if (null !== $privateKeyPassphrase) { + $this->signPrivateKey = [$this->normalizeFilePath($privateKey), $privateKeyPassphrase]; + } else { + $this->signPrivateKey = $this->normalizeFilePath($privateKey); + } + + $this->signOptions = $signOptions ?? PKCS7_DETACHED; + $this->extraCerts = $extraCerts ? realpath($extraCerts) : null; + $this->privateKeyPassphrase = $privateKeyPassphrase; + } + + public function sign(Message $message): Message + { + $bufferFile = tmpfile(); + $outputFile = tmpfile(); + + $this->iteratorToFile($message->getBody()->toIterable(), $bufferFile); + + if (!@openssl_pkcs7_sign(stream_get_meta_data($bufferFile)['uri'], stream_get_meta_data($outputFile)['uri'], $this->signCertificate, $this->signPrivateKey, [], $this->signOptions, $this->extraCerts)) { + throw new RuntimeException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())); + } + + return new Message($message->getHeaders(), $this->convertMessageToSMimePart($outputFile, 'multipart', 'signed')); + } +} diff --git a/vendor/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php b/vendor/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php new file mode 100644 index 0000000..e24beb0 --- /dev/null +++ b/vendor/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers custom mime types guessers. + * + * @author Fabien Potencier + */ +class AddMimeTypeGuesserPass implements CompilerPassInterface +{ + private $mimeTypesService; + private $mimeTypeGuesserTag; + + public function __construct(string $mimeTypesService = 'mime_types', string $mimeTypeGuesserTag = 'mime.mime_type_guesser') + { + $this->mimeTypesService = $mimeTypesService; + $this->mimeTypeGuesserTag = $mimeTypeGuesserTag; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if ($container->has($this->mimeTypesService)) { + $definition = $container->findDefinition($this->mimeTypesService); + foreach ($container->findTaggedServiceIds($this->mimeTypeGuesserTag, true) as $id => $attributes) { + $definition->addMethodCall('registerGuesser', [new Reference($id)]); + } + } + } +} diff --git a/vendor/symfony/mime/Email.php b/vendor/symfony/mime/Email.php new file mode 100644 index 0000000..fb91262 --- /dev/null +++ b/vendor/symfony/mime/Email.php @@ -0,0 +1,599 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\LogicException; +use Symfony\Component\Mime\Part\AbstractPart; +use Symfony\Component\Mime\Part\DataPart; +use Symfony\Component\Mime\Part\Multipart\AlternativePart; +use Symfony\Component\Mime\Part\Multipart\MixedPart; +use Symfony\Component\Mime\Part\Multipart\RelatedPart; +use Symfony\Component\Mime\Part\TextPart; + +/** + * @author Fabien Potencier + */ +class Email extends Message +{ + const PRIORITY_HIGHEST = 1; + const PRIORITY_HIGH = 2; + const PRIORITY_NORMAL = 3; + const PRIORITY_LOW = 4; + const PRIORITY_LOWEST = 5; + + private const PRIORITY_MAP = [ + self::PRIORITY_HIGHEST => 'Highest', + self::PRIORITY_HIGH => 'High', + self::PRIORITY_NORMAL => 'Normal', + self::PRIORITY_LOW => 'Low', + self::PRIORITY_LOWEST => 'Lowest', + ]; + + private $text; + private $textCharset; + private $html; + private $htmlCharset; + private $attachments = []; + + /** + * @return $this + */ + public function subject(string $subject) + { + return $this->setHeaderBody('Text', 'Subject', $subject); + } + + public function getSubject(): ?string + { + return $this->getHeaders()->getHeaderBody('Subject'); + } + + /** + * @return $this + */ + public function date(\DateTimeInterface $dateTime) + { + return $this->setHeaderBody('Date', 'Date', $dateTime); + } + + public function getDate(): ?\DateTimeImmutable + { + return $this->getHeaders()->getHeaderBody('Date'); + } + + /** + * @param Address|string $address + * + * @return $this + */ + public function returnPath($address) + { + return $this->setHeaderBody('Path', 'Return-Path', Address::create($address)); + } + + public function getReturnPath(): ?Address + { + return $this->getHeaders()->getHeaderBody('Return-Path'); + } + + /** + * @param Address|string $address + * + * @return $this + */ + public function sender($address) + { + return $this->setHeaderBody('Mailbox', 'Sender', Address::create($address)); + } + + public function getSender(): ?Address + { + return $this->getHeaders()->getHeaderBody('Sender'); + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function addFrom(...$addresses) + { + return $this->addListAddressHeaderBody('From', $addresses); + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function from(...$addresses) + { + return $this->setListAddressHeaderBody('From', $addresses); + } + + /** + * @return Address[] + */ + public function getFrom(): array + { + return $this->getHeaders()->getHeaderBody('From') ?: []; + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function addReplyTo(...$addresses) + { + return $this->addListAddressHeaderBody('Reply-To', $addresses); + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function replyTo(...$addresses) + { + return $this->setListAddressHeaderBody('Reply-To', $addresses); + } + + /** + * @return Address[] + */ + public function getReplyTo(): array + { + return $this->getHeaders()->getHeaderBody('Reply-To') ?: []; + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function addTo(...$addresses) + { + return $this->addListAddressHeaderBody('To', $addresses); + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function to(...$addresses) + { + return $this->setListAddressHeaderBody('To', $addresses); + } + + /** + * @return Address[] + */ + public function getTo(): array + { + return $this->getHeaders()->getHeaderBody('To') ?: []; + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function addCc(...$addresses) + { + return $this->addListAddressHeaderBody('Cc', $addresses); + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function cc(...$addresses) + { + return $this->setListAddressHeaderBody('Cc', $addresses); + } + + /** + * @return Address[] + */ + public function getCc(): array + { + return $this->getHeaders()->getHeaderBody('Cc') ?: []; + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function addBcc(...$addresses) + { + return $this->addListAddressHeaderBody('Bcc', $addresses); + } + + /** + * @param Address|string ...$addresses + * + * @return $this + */ + public function bcc(...$addresses) + { + return $this->setListAddressHeaderBody('Bcc', $addresses); + } + + /** + * @return Address[] + */ + public function getBcc(): array + { + return $this->getHeaders()->getHeaderBody('Bcc') ?: []; + } + + /** + * Sets the priority of this message. + * + * The value is an integer where 1 is the highest priority and 5 is the lowest. + * + * @return $this + */ + public function priority(int $priority) + { + if ($priority > 5) { + $priority = 5; + } elseif ($priority < 1) { + $priority = 1; + } + + return $this->setHeaderBody('Text', 'X-Priority', sprintf('%d (%s)', $priority, self::PRIORITY_MAP[$priority])); + } + + /** + * Get the priority of this message. + * + * The returned value is an integer where 1 is the highest priority and 5 + * is the lowest. + */ + public function getPriority(): int + { + list($priority) = sscanf($this->getHeaders()->getHeaderBody('X-Priority'), '%[1-5]'); + + return $priority ?? 3; + } + + /** + * @param resource|string $body + * + * @return $this + */ + public function text($body, string $charset = 'utf-8') + { + $this->text = $body; + $this->textCharset = $charset; + + return $this; + } + + /** + * @return resource|string|null + */ + public function getTextBody() + { + return $this->text; + } + + public function getTextCharset(): ?string + { + return $this->textCharset; + } + + /** + * @param resource|string|null $body + * + * @return $this + */ + public function html($body, string $charset = 'utf-8') + { + $this->html = $body; + $this->htmlCharset = $charset; + + return $this; + } + + /** + * @return resource|string|null + */ + public function getHtmlBody() + { + return $this->html; + } + + public function getHtmlCharset(): ?string + { + return $this->htmlCharset; + } + + /** + * @param resource|string $body + * + * @return $this + */ + public function attach($body, string $name = null, string $contentType = null) + { + $this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => false]; + + return $this; + } + + /** + * @return $this + */ + public function attachFromPath(string $path, string $name = null, string $contentType = null) + { + $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => false]; + + return $this; + } + + /** + * @param resource|string $body + * + * @return $this + */ + public function embed($body, string $name = null, string $contentType = null) + { + $this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => true]; + + return $this; + } + + /** + * @return $this + */ + public function embedFromPath(string $path, string $name = null, string $contentType = null) + { + $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => true]; + + return $this; + } + + /** + * @return $this + */ + public function attachPart(DataPart $part) + { + $this->attachments[] = ['part' => $part]; + + return $this; + } + + /** + * @return DataPart[] + */ + public function getAttachments(): array + { + $parts = []; + foreach ($this->attachments as $attachment) { + $parts[] = $this->createDataPart($attachment); + } + + return $parts; + } + + public function getBody(): AbstractPart + { + if (null !== $body = parent::getBody()) { + return $body; + } + + return $this->generateBody(); + } + + public function ensureValidity() + { + if (null === $this->text && null === $this->html && !$this->attachments) { + throw new LogicException('A message must have a text or an HTML part or attachments.'); + } + + parent::ensureValidity(); + } + + /** + * Generates an AbstractPart based on the raw body of a message. + * + * The most "complex" part generated by this method is when there is text and HTML bodies + * with related images for the HTML part and some attachments: + * + * multipart/mixed + * | + * |------------> multipart/related + * | | + * | |------------> multipart/alternative + * | | | + * | | ------------> text/plain (with content) + * | | | + * | | ------------> text/html (with content) + * | | + * | ------------> image/png (with content) + * | + * ------------> application/pdf (with content) + */ + private function generateBody(): AbstractPart + { + $this->ensureValidity(); + + [$htmlPart, $attachmentParts, $inlineParts] = $this->prepareParts(); + + $part = null === $this->text ? null : new TextPart($this->text, $this->textCharset); + if (null !== $htmlPart) { + if (null !== $part) { + $part = new AlternativePart($part, $htmlPart); + } else { + $part = $htmlPart; + } + } + + if ($inlineParts) { + $part = new RelatedPart($part, ...$inlineParts); + } + + if ($attachmentParts) { + if ($part) { + $part = new MixedPart($part, ...$attachmentParts); + } else { + $part = new MixedPart(...$attachmentParts); + } + } + + return $part; + } + + private function prepareParts(): ?array + { + $names = []; + $htmlPart = null; + $html = $this->html; + if (null !== $this->html) { + if (\is_resource($html)) { + if (stream_get_meta_data($html)['seekable'] ?? false) { + rewind($html); + } + + $html = stream_get_contents($html); + } + $htmlPart = new TextPart($html, $this->htmlCharset, 'html'); + preg_match_all('(]*src\s*=\s*(?:([\'"])cid:([^"]+)\\1|cid:([^>\s]+)))i', $html, $names); + $names = array_filter(array_unique(array_merge($names[2], $names[3]))); + } + + $attachmentParts = $inlineParts = []; + foreach ($this->attachments as $attachment) { + foreach ($names as $name) { + if (isset($attachment['part'])) { + continue; + } + if ($name !== $attachment['name']) { + continue; + } + if (isset($inlineParts[$name])) { + continue 2; + } + $attachment['inline'] = true; + $inlineParts[$name] = $part = $this->createDataPart($attachment); + $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html); + continue 2; + } + $attachmentParts[] = $this->createDataPart($attachment); + } + if (null !== $htmlPart) { + $htmlPart = new TextPart($html, $this->htmlCharset, 'html'); + } + + return [$htmlPart, $attachmentParts, array_values($inlineParts)]; + } + + private function createDataPart(array $attachment): DataPart + { + if (isset($attachment['part'])) { + return $attachment['part']; + } + + if (isset($attachment['body'])) { + $part = new DataPart($attachment['body'], $attachment['name'] ?? null, $attachment['content-type'] ?? null); + } else { + $part = DataPart::fromPath($attachment['path'] ?? '', $attachment['name'] ?? null, $attachment['content-type'] ?? null); + } + if ($attachment['inline']) { + $part->asInline(); + } + + return $part; + } + + /** + * @return $this + */ + private function setHeaderBody(string $type, string $name, $body): object + { + $this->getHeaders()->setHeaderBody($type, $name, $body); + + return $this; + } + + private function addListAddressHeaderBody(string $name, array $addresses) + { + if (!$header = $this->getHeaders()->get($name)) { + return $this->setListAddressHeaderBody($name, $addresses); + } + $header->addAddresses(Address::createArray($addresses)); + + return $this; + } + + private function setListAddressHeaderBody(string $name, array $addresses) + { + $addresses = Address::createArray($addresses); + $headers = $this->getHeaders(); + if ($header = $headers->get($name)) { + $header->setAddresses($addresses); + } else { + $headers->addMailboxListHeader($name, $addresses); + } + + return $this; + } + + /** + * @internal + */ + public function __serialize(): array + { + if (\is_resource($this->text)) { + if (stream_get_meta_data($this->text)['seekable'] ?? false) { + rewind($this->text); + } + + $this->text = stream_get_contents($this->text); + } + + if (\is_resource($this->html)) { + if (stream_get_meta_data($this->html)['seekable'] ?? false) { + rewind($this->html); + } + + $this->html = stream_get_contents($this->html); + } + + foreach ($this->attachments as $i => $attachment) { + if (isset($attachment['body']) && \is_resource($attachment['body'])) { + if (stream_get_meta_data($attachment['body'])['seekable'] ?? false) { + rewind($attachment['body']); + } + + $this->attachments[$i]['body'] = stream_get_contents($attachment['body']); + } + } + + return [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, parent::__serialize()]; + } + + /** + * @internal + */ + public function __unserialize(array $data): void + { + [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, $parentData] = $data; + + parent::__unserialize($parentData); + } +} diff --git a/vendor/symfony/mime/Encoder/AddressEncoderInterface.php b/vendor/symfony/mime/Encoder/AddressEncoderInterface.php new file mode 100644 index 0000000..de477d8 --- /dev/null +++ b/vendor/symfony/mime/Encoder/AddressEncoderInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +use Symfony\Component\Mime\Exception\AddressEncoderException; + +/** + * @author Christian Schmidt + */ +interface AddressEncoderInterface +{ + /** + * Encodes an email address. + * + * @throws AddressEncoderException if the email cannot be represented in + * the encoding implemented by this class + */ + public function encodeString(string $address): string; +} diff --git a/vendor/symfony/mime/Encoder/Base64ContentEncoder.php b/vendor/symfony/mime/Encoder/Base64ContentEncoder.php new file mode 100644 index 0000000..cb7f911 --- /dev/null +++ b/vendor/symfony/mime/Encoder/Base64ContentEncoder.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +use Symfony\Component\Mime\Exception\RuntimeException; + +/** + * @author Fabien Potencier + */ +final class Base64ContentEncoder extends Base64Encoder implements ContentEncoderInterface +{ + public function encodeByteStream($stream, int $maxLineLength = 0): iterable + { + if (!\is_resource($stream)) { + throw new \TypeError(sprintf('Method "%s" takes a stream as a first argument.', __METHOD__)); + } + + $filter = stream_filter_append($stream, 'convert.base64-encode', STREAM_FILTER_READ, [ + 'line-length' => 0 >= $maxLineLength || 76 < $maxLineLength ? 76 : $maxLineLength, + 'line-break-chars' => "\r\n", + ]); + if (!\is_resource($filter)) { + throw new RuntimeException('Unable to set the base64 content encoder to the filter.'); + } + + if (stream_get_meta_data($stream)['seekable'] ?? false) { + rewind($stream); + } + while (!feof($stream)) { + yield fread($stream, 8192); + } + stream_filter_remove($filter); + } + + public function getName(): string + { + return 'base64'; + } +} diff --git a/vendor/symfony/mime/Encoder/Base64Encoder.php b/vendor/symfony/mime/Encoder/Base64Encoder.php new file mode 100644 index 0000000..7106478 --- /dev/null +++ b/vendor/symfony/mime/Encoder/Base64Encoder.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Chris Corbyn + */ +class Base64Encoder implements EncoderInterface +{ + /** + * Takes an unencoded string and produces a Base64 encoded string from it. + * + * Base64 encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + */ + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + if (0 >= $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $encodedString = base64_encode($string); + $firstLine = ''; + if (0 !== $firstLineOffset) { + $firstLine = substr($encodedString, 0, $maxLineLength - $firstLineOffset)."\r\n"; + $encodedString = substr($encodedString, $maxLineLength - $firstLineOffset); + } + + return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n")); + } +} diff --git a/vendor/symfony/mime/Encoder/Base64MimeHeaderEncoder.php b/vendor/symfony/mime/Encoder/Base64MimeHeaderEncoder.php new file mode 100644 index 0000000..5c06f6d --- /dev/null +++ b/vendor/symfony/mime/Encoder/Base64MimeHeaderEncoder.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Chris Corbyn + */ +final class Base64MimeHeaderEncoder extends Base64Encoder implements MimeHeaderEncoderInterface +{ + public function getName(): string + { + return 'B'; + } + + /** + * Takes an unencoded string and produces a Base64 encoded string from it. + * + * If the charset is iso-2022-jp, it uses mb_encode_mimeheader instead of + * default encodeString, otherwise pass to the parent method. + */ + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + if ('iso-2022-jp' === strtolower($charset)) { + $old = mb_internal_encoding(); + mb_internal_encoding('utf-8'); + $newstring = mb_encode_mimeheader($string, 'iso-2022-jp', $this->getName(), "\r\n"); + mb_internal_encoding($old); + + return $newstring; + } + + return parent::encodeString($string, $charset, $firstLineOffset, $maxLineLength); + } +} diff --git a/vendor/symfony/mime/Encoder/ContentEncoderInterface.php b/vendor/symfony/mime/Encoder/ContentEncoderInterface.php new file mode 100644 index 0000000..a45ad04 --- /dev/null +++ b/vendor/symfony/mime/Encoder/ContentEncoderInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Chris Corbyn + */ +interface ContentEncoderInterface extends EncoderInterface +{ + /** + * Encodes the stream to a Generator. + * + * @param resource $stream + */ + public function encodeByteStream($stream, int $maxLineLength = 0): iterable; + + /** + * Gets the MIME name of this content encoding scheme. + */ + public function getName(): string; +} diff --git a/vendor/symfony/mime/Encoder/EightBitContentEncoder.php b/vendor/symfony/mime/Encoder/EightBitContentEncoder.php new file mode 100644 index 0000000..8283120 --- /dev/null +++ b/vendor/symfony/mime/Encoder/EightBitContentEncoder.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Fabien Potencier + */ +final class EightBitContentEncoder implements ContentEncoderInterface +{ + public function encodeByteStream($stream, int $maxLineLength = 0): iterable + { + while (!feof($stream)) { + yield fread($stream, 16372); + } + } + + public function getName(): string + { + return '8bit'; + } + + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + return $string; + } +} diff --git a/vendor/symfony/mime/Encoder/EncoderInterface.php b/vendor/symfony/mime/Encoder/EncoderInterface.php new file mode 100644 index 0000000..bbf6d48 --- /dev/null +++ b/vendor/symfony/mime/Encoder/EncoderInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Chris Corbyn + */ +interface EncoderInterface +{ + /** + * Encode a given string to produce an encoded string. + * + * @param int $firstLineOffset if first line needs to be shorter + * @param int $maxLineLength - 0 indicates the default length for this encoding + */ + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string; +} diff --git a/vendor/symfony/mime/Encoder/IdnAddressEncoder.php b/vendor/symfony/mime/Encoder/IdnAddressEncoder.php new file mode 100644 index 0000000..1c5e32c --- /dev/null +++ b/vendor/symfony/mime/Encoder/IdnAddressEncoder.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +use Symfony\Component\Mime\Exception\AddressEncoderException; + +/** + * An IDN email address encoder. + * + * Encodes the domain part of an address using IDN. This is compatible will all + * SMTP servers. + * + * This encoder does not support email addresses with non-ASCII characters in + * local-part (the substring before @). To send to such addresses, use + * Utf8AddressEncoder together with SmtpUtf8Handler. Your outbound SMTP server must support + * the SMTPUTF8 extension. + * + * @author Christian Schmidt + */ +final class IdnAddressEncoder implements AddressEncoderInterface +{ + /** + * Encodes the domain part of an address using IDN. + * + * @throws AddressEncoderException If local-part contains non-ASCII characters + */ + public function encodeString(string $address): string + { + $i = strrpos($address, '@'); + if (false !== $i) { + $local = substr($address, 0, $i); + $domain = substr($address, $i + 1); + + if (preg_match('/[^\x00-\x7F]/', $local)) { + throw new AddressEncoderException(sprintf('Non-ASCII characters not supported in local-part os "%s".', $address)); + } + + if (preg_match('/[^\x00-\x7F]/', $domain)) { + $address = sprintf('%s@%s', $local, idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46)); + } + } + + return $address; + } +} diff --git a/vendor/symfony/mime/Encoder/MimeHeaderEncoderInterface.php b/vendor/symfony/mime/Encoder/MimeHeaderEncoderInterface.php new file mode 100644 index 0000000..fff2c78 --- /dev/null +++ b/vendor/symfony/mime/Encoder/MimeHeaderEncoderInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Chris Corbyn + */ +interface MimeHeaderEncoderInterface +{ + /** + * Get the MIME name of this content encoding scheme. + */ + public function getName(): string; +} diff --git a/vendor/symfony/mime/Encoder/QpContentEncoder.php b/vendor/symfony/mime/Encoder/QpContentEncoder.php new file mode 100644 index 0000000..e0b8605 --- /dev/null +++ b/vendor/symfony/mime/Encoder/QpContentEncoder.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Lars Strojny + */ +final class QpContentEncoder implements ContentEncoderInterface +{ + public function encodeByteStream($stream, int $maxLineLength = 0): iterable + { + if (!\is_resource($stream)) { + throw new \TypeError(sprintf('Method "%s" takes a stream as a first argument.', __METHOD__)); + } + + // we don't use PHP stream filters here as the content should be small enough + if (stream_get_meta_data($stream)['seekable'] ?? false) { + rewind($stream); + } + + yield $this->encodeString(stream_get_contents($stream), 'utf-8', 0, $maxLineLength); + } + + public function getName(): string + { + return 'quoted-printable'; + } + + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + return $this->standardize(quoted_printable_encode($string)); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + */ + private function standardize(string $string): string + { + // transform CR or LF to CRLF + $string = preg_replace('~=0D(?!=0A)|(? + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +use Symfony\Component\Mime\CharacterStream; + +/** + * @author Chris Corbyn + */ +class QpEncoder implements EncoderInterface +{ + /** + * Pre-computed QP for HUGE optimization. + */ + private static $qpMap = [ + 0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', + 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', + 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', + 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13', + 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18', + 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D', + 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22', + 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27', + 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C', + 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31', + 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36', + 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B', + 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40', + 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45', + 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A', + 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F', + 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54', + 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59', + 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E', + 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63', + 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68', + 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D', + 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72', + 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77', + 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C', + 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81', + 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86', + 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B', + 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90', + 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95', + 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A', + 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F', + 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4', + 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9', + 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE', + 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3', + 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8', + 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD', + 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2', + 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7', + 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC', + 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1', + 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6', + 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB', + 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0', + 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5', + 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA', + 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF', + 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4', + 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9', + 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE', + 255 => '=FF', + ]; + + private static $safeMapShare = []; + + /** + * A map of non-encoded ascii characters. + * + * @var string[] + * + * @internal + */ + protected $safeMap = []; + + public function __construct() + { + $id = static::class; + if (!isset(self::$safeMapShare[$id])) { + $this->initSafeMap(); + self::$safeMapShare[$id] = $this->safeMap; + } else { + $this->safeMap = self::$safeMapShare[$id]; + } + } + + protected function initSafeMap(): void + { + foreach (array_merge([0x09, 0x20], range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) { + $this->safeMap[$byte] = \chr($byte); + } + } + + /** + * {@inheritdoc} + * + * Takes an unencoded string and produces a QP encoded string from it. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + */ + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $lines = []; + $lNo = 0; + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $size = $lineLen = 0; + $charStream = new CharacterStream($string, $charset); + + // Fetching more than 4 chars at one is slower, as is fetching fewer bytes + // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 + // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes + while (null !== $bytes = $charStream->readBytes(4)) { + $enc = $this->encodeByteSequence($bytes, $size); + + $i = strpos($enc, '=0D=0A'); + $newLineLength = $lineLen + (false === $i ? $size : $i); + + if ($currentLine && $newLineLength >= $thisLineLength) { + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + + $currentLine .= $enc; + + if (false === $i) { + $lineLen += $size; + } else { + // 6 is the length of '=0D=0A'. + $lineLen = $size - strrpos($enc, '=0D=0A') - 6; + } + } + + return $this->standardize(implode("=\r\n", $lines)); + } + + /** + * Encode the given byte array into a verbatim QP form. + */ + private function encodeByteSequence(array $bytes, int &$size): string + { + $ret = ''; + $size = 0; + foreach ($bytes as $b) { + if (isset($this->safeMap[$b])) { + $ret .= $this->safeMap[$b]; + ++$size; + } else { + $ret .= self::$qpMap[$b]; + $size += 3; + } + } + + return $ret; + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + */ + private function standardize(string $string): string + { + $string = str_replace(["\t=0D=0A", ' =0D=0A', '=0D=0A'], ["=09\r\n", "=20\r\n", "\r\n"], $string); + switch ($end = \ord(substr($string, -1))) { + case 0x09: + case 0x20: + $string = substr_replace($string, self::$qpMap[$end], -1); + } + + return $string; + } +} diff --git a/vendor/symfony/mime/Encoder/QpMimeHeaderEncoder.php b/vendor/symfony/mime/Encoder/QpMimeHeaderEncoder.php new file mode 100644 index 0000000..d1d3837 --- /dev/null +++ b/vendor/symfony/mime/Encoder/QpMimeHeaderEncoder.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +/** + * @author Chris Corbyn + */ +final class QpMimeHeaderEncoder extends QpEncoder implements MimeHeaderEncoderInterface +{ + protected function initSafeMap(): void + { + foreach (array_merge( + range(0x61, 0x7A), range(0x41, 0x5A), + range(0x30, 0x39), [0x20, 0x21, 0x2A, 0x2B, 0x2D, 0x2F] + ) as $byte) { + $this->safeMap[$byte] = \chr($byte); + } + } + + public function getName(): string + { + return 'Q'; + } + + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + return str_replace([' ', '=20', "=\r\n"], ['_', '_', "\r\n"], + parent::encodeString($string, $charset, $firstLineOffset, $maxLineLength) + ); + } +} diff --git a/vendor/symfony/mime/Encoder/Rfc2231Encoder.php b/vendor/symfony/mime/Encoder/Rfc2231Encoder.php new file mode 100644 index 0000000..aa3e062 --- /dev/null +++ b/vendor/symfony/mime/Encoder/Rfc2231Encoder.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Encoder; + +use Symfony\Component\Mime\CharacterStream; + +/** + * @author Chris Corbyn + */ +final class Rfc2231Encoder implements EncoderInterface +{ + /** + * Takes an unencoded string and produces a string encoded according to RFC 2231 from it. + */ + public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string + { + $lines = []; + $lineCount = 0; + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + if (0 >= $maxLineLength) { + $maxLineLength = 75; + } + + $charStream = new CharacterStream($string, $charset); + $thisLineLength = $maxLineLength - $firstLineOffset; + + while (null !== $char = $charStream->read(4)) { + $encodedChar = rawurlencode($char); + if (0 !== \strlen($currentLine) && \strlen($currentLine.$encodedChar) > $thisLineLength) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + $thisLineLength = $maxLineLength; + } + $currentLine .= $encodedChar; + } + + return implode("\r\n", $lines); + } +} diff --git a/vendor/symfony/mime/Exception/AddressEncoderException.php b/vendor/symfony/mime/Exception/AddressEncoderException.php new file mode 100644 index 0000000..51ee2e0 --- /dev/null +++ b/vendor/symfony/mime/Exception/AddressEncoderException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Exception; + +/** + * @author Fabien Potencier + */ +class AddressEncoderException extends RfcComplianceException +{ +} diff --git a/vendor/symfony/mime/Exception/ExceptionInterface.php b/vendor/symfony/mime/Exception/ExceptionInterface.php new file mode 100644 index 0000000..1193390 --- /dev/null +++ b/vendor/symfony/mime/Exception/ExceptionInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Exception; + +/** + * @author Fabien Potencier + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/vendor/symfony/mime/Exception/InvalidArgumentException.php b/vendor/symfony/mime/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..e89ebae --- /dev/null +++ b/vendor/symfony/mime/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Exception; + +/** + * @author Fabien Potencier + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/mime/Exception/LogicException.php b/vendor/symfony/mime/Exception/LogicException.php new file mode 100644 index 0000000..0508ee7 --- /dev/null +++ b/vendor/symfony/mime/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Exception; + +/** + * @author Fabien Potencier + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/mime/Exception/RfcComplianceException.php b/vendor/symfony/mime/Exception/RfcComplianceException.php new file mode 100644 index 0000000..26e4a50 --- /dev/null +++ b/vendor/symfony/mime/Exception/RfcComplianceException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Exception; + +/** + * @author Fabien Potencier + */ +class RfcComplianceException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/mime/Exception/RuntimeException.php b/vendor/symfony/mime/Exception/RuntimeException.php new file mode 100644 index 0000000..fb018b0 --- /dev/null +++ b/vendor/symfony/mime/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Exception; + +/** + * @author Fabien Potencier + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/mime/FileBinaryMimeTypeGuesser.php b/vendor/symfony/mime/FileBinaryMimeTypeGuesser.php new file mode 100644 index 0000000..fe1e0cd --- /dev/null +++ b/vendor/symfony/mime/FileBinaryMimeTypeGuesser.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Exception\LogicException; + +/** + * Guesses the MIME type with the binary "file" (only available on *nix). + * + * @author Bernhard Schussek + */ +class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $cmd; + + /** + * The $cmd pattern must contain a "%s" string that will be replaced + * with the file name to guess. + * + * The command output must start with the MIME type of the file. + * + * @param string $cmd The command to run to get the MIME type of a file + */ + public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null') + { + $this->cmd = $cmd; + } + + /** + * {@inheritdoc} + */ + public function isGuesserSupported(): bool + { + static $supported = null; + + if (null !== $supported) { + return $supported; + } + + if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) { + return $supported = false; + } + + ob_start(); + passthru('command -v file', $exitStatus); + $binPath = trim(ob_get_clean()); + + return $supported = 0 === $exitStatus && '' !== $binPath; + } + + /** + * {@inheritdoc} + */ + public function guessMimeType(string $path): ?string + { + if (!is_file($path) || !is_readable($path)) { + throw new InvalidArgumentException(sprintf('The "%s" file does not exist or is not readable.', $path)); + } + + if (!$this->isGuesserSupported()) { + throw new LogicException(sprintf('The "%s" guesser is not supported.', __CLASS__)); + } + + ob_start(); + + // need to use --mime instead of -i. see #6641 + passthru(sprintf($this->cmd, escapeshellarg((0 === strpos($path, '-') ? './' : '').$path)), $return); + if ($return > 0) { + ob_end_clean(); + + return null; + } + + $type = trim(ob_get_clean()); + + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) { + // it's not a type, but an error message + return null; + } + + return $match[1]; + } +} diff --git a/vendor/symfony/mime/FileinfoMimeTypeGuesser.php b/vendor/symfony/mime/FileinfoMimeTypeGuesser.php new file mode 100644 index 0000000..b91a4ff --- /dev/null +++ b/vendor/symfony/mime/FileinfoMimeTypeGuesser.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Exception\LogicException; + +/** + * Guesses the MIME type using the PECL extension FileInfo. + * + * @author Bernhard Schussek + */ +class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $magicFile; + + /** + * @param string $magicFile A magic file to use with the finfo instance + * + * @see http://www.php.net/manual/en/function.finfo-open.php + */ + public function __construct(string $magicFile = null) + { + $this->magicFile = $magicFile; + } + + /** + * {@inheritdoc} + */ + public function isGuesserSupported(): bool + { + return \function_exists('finfo_open'); + } + + /** + * {@inheritdoc} + */ + public function guessMimeType(string $path): ?string + { + if (!is_file($path) || !is_readable($path)) { + throw new InvalidArgumentException(sprintf('The "%s" file does not exist or is not readable.', $path)); + } + + if (!$this->isGuesserSupported()) { + throw new LogicException(sprintf('The "%s" guesser is not supported.', __CLASS__)); + } + + if (false === $finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) { + return null; + } + + return $finfo->file($path); + } +} diff --git a/vendor/symfony/mime/Header/AbstractHeader.php b/vendor/symfony/mime/Header/AbstractHeader.php new file mode 100644 index 0000000..548c192 --- /dev/null +++ b/vendor/symfony/mime/Header/AbstractHeader.php @@ -0,0 +1,279 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Encoder\QpMimeHeaderEncoder; + +/** + * An abstract base MIME Header. + * + * @author Chris Corbyn + */ +abstract class AbstractHeader implements HeaderInterface +{ + const PHRASE_PATTERN = '(?:(?:(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]+(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?)|(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?"((?:(?:[ \t]*(?:\r\n))?[ \t])?(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21\x23-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])))*(?:(?:[ \t]*(?:\r\n))?[ \t])?"(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?))+?)'; + + private static $encoder; + + private $name; + private $lineLength = 76; + private $lang; + private $charset = 'utf-8'; + + public function __construct(string $name) + { + $this->name = $name; + } + + public function setCharset(string $charset) + { + $this->charset = $charset; + } + + public function getCharset(): ?string + { + return $this->charset; + } + + /** + * Set the language used in this Header. + * + * For example, for US English, 'en-us'. + */ + public function setLanguage(string $lang) + { + $this->lang = $lang; + } + + public function getLanguage(): ?string + { + return $this->lang; + } + + public function getName(): string + { + return $this->name; + } + + public function setMaxLineLength(int $lineLength) + { + $this->lineLength = $lineLength; + } + + public function getMaxLineLength(): int + { + return $this->lineLength; + } + + public function toString(): string + { + return $this->tokensToString($this->toTokens()); + } + + /** + * Produces a compliant, formatted RFC 2822 'phrase' based on the string given. + * + * @param string $string as displayed + * @param bool $shorten the first line to make remove for header name + */ + protected function createPhrase(HeaderInterface $header, string $string, string $charset, bool $shorten = false): string + { + // Treat token as exactly what was given + $phraseStr = $string; + + // If it's not valid + if (!preg_match('/^'.self::PHRASE_PATTERN.'$/D', $phraseStr)) { + // .. but it is just ascii text, try escaping some characters + // and make it a quoted-string + if (preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $phraseStr)) { + foreach (['\\', '"'] as $char) { + $phraseStr = str_replace($char, '\\'.$char, $phraseStr); + } + $phraseStr = '"'.$phraseStr.'"'; + } else { + // ... otherwise it needs encoding + // Determine space remaining on line if first line + if ($shorten) { + $usedLength = \strlen($header->getName().': '); + } else { + $usedLength = 0; + } + $phraseStr = $this->encodeWords($header, $string, $usedLength); + } + } + + return $phraseStr; + } + + /** + * Encode needed word tokens within a string of input. + */ + protected function encodeWords(HeaderInterface $header, string $input, int $usedLength = -1): string + { + $value = ''; + $tokens = $this->getEncodableWordTokens($input); + foreach ($tokens as $token) { + // See RFC 2822, Sect 2.2 (really 2.2 ??) + if ($this->tokenNeedsEncoding($token)) { + // Don't encode starting WSP + $firstChar = substr($token, 0, 1); + switch ($firstChar) { + case ' ': + case "\t": + $value .= $firstChar; + $token = substr($token, 1); + } + + if (-1 == $usedLength) { + $usedLength = \strlen($header->getName().': ') + \strlen($value); + } + $value .= $this->getTokenAsEncodedWord($token, $usedLength); + } else { + $value .= $token; + } + } + + return $value; + } + + protected function tokenNeedsEncoding(string $token): bool + { + return (bool) preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token); + } + + /** + * Splits a string into tokens in blocks of words which can be encoded quickly. + * + * @return string[] + */ + protected function getEncodableWordTokens(string $string): array + { + $tokens = []; + $encodedToken = ''; + // Split at all whitespace boundaries + foreach (preg_split('~(?=[\t ])~', $string) as $token) { + if ($this->tokenNeedsEncoding($token)) { + $encodedToken .= $token; + } else { + if (\strlen($encodedToken) > 0) { + $tokens[] = $encodedToken; + $encodedToken = ''; + } + $tokens[] = $token; + } + } + if (\strlen($encodedToken)) { + $tokens[] = $encodedToken; + } + + return $tokens; + } + + /** + * Get a token as an encoded word for safe insertion into headers. + */ + protected function getTokenAsEncodedWord(string $token, int $firstLineOffset = 0): string + { + if (null === self::$encoder) { + self::$encoder = new QpMimeHeaderEncoder(); + } + + // Adjust $firstLineOffset to account for space needed for syntax + $charsetDecl = $this->charset; + if (null !== $this->lang) { + $charsetDecl .= '*'.$this->lang; + } + $encodingWrapperLength = \strlen('=?'.$charsetDecl.'?'.self::$encoder->getName().'??='); + + if ($firstLineOffset >= 75) { + //Does this logic need to be here? + $firstLineOffset = 0; + } + + $encodedTextLines = explode("\r\n", + self::$encoder->encodeString($token, $this->charset, $firstLineOffset, 75 - $encodingWrapperLength) + ); + + if ('iso-2022-jp' !== strtolower($this->charset)) { + // special encoding for iso-2022-jp using mb_encode_mimeheader + foreach ($encodedTextLines as $lineNum => $line) { + $encodedTextLines[$lineNum] = '=?'.$charsetDecl.'?'.self::$encoder->getName().'?'.$line.'?='; + } + } + + return implode("\r\n ", $encodedTextLines); + } + + /** + * Generates tokens from the given string which include CRLF as individual tokens. + * + * @return string[] + */ + protected function generateTokenLines(string $token): array + { + return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE); + } + + /** + * Generate a list of all tokens in the final header. + */ + protected function toTokens(string $string = null): array + { + if (null === $string) { + $string = $this->getBodyAsString(); + } + + $tokens = []; + // Generate atoms; split at all invisible boundaries followed by WSP + foreach (preg_split('~(?=[ \t])~', $string) as $token) { + $newTokens = $this->generateTokenLines($token); + foreach ($newTokens as $newToken) { + $tokens[] = $newToken; + } + } + + return $tokens; + } + + /** + * Takes an array of tokens which appear in the header and turns them into + * an RFC 2822 compliant string, adding FWSP where needed. + * + * @param string[] $tokens + */ + private function tokensToString(array $tokens): string + { + $lineCount = 0; + $headerLines = []; + $headerLines[] = $this->name.': '; + $currentLine = &$headerLines[$lineCount++]; + + // Build all tokens back into compliant header + foreach ($tokens as $i => $token) { + // Line longer than specified maximum or token was just a new line + if (("\r\n" === $token) || + ($i > 0 && \strlen($currentLine.$token) > $this->lineLength) + && 0 < \strlen($currentLine)) { + $headerLines[] = ''; + $currentLine = &$headerLines[$lineCount++]; + } + + // Append token to the line + if ("\r\n" !== $token) { + $currentLine .= $token; + } + } + + // Implode with FWS (RFC 2822, 2.2.3) + return implode("\r\n", $headerLines); + } +} diff --git a/vendor/symfony/mime/Header/DateHeader.php b/vendor/symfony/mime/Header/DateHeader.php new file mode 100644 index 0000000..a7385d4 --- /dev/null +++ b/vendor/symfony/mime/Header/DateHeader.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +/** + * A Date MIME Header. + * + * @author Chris Corbyn + */ +final class DateHeader extends AbstractHeader +{ + private $dateTime; + + public function __construct(string $name, \DateTimeInterface $date) + { + parent::__construct($name); + + $this->setDateTime($date); + } + + /** + * @param \DateTimeInterface $body + */ + public function setBody($body) + { + $this->setDateTime($body); + } + + public function getBody(): \DateTimeImmutable + { + return $this->getDateTime(); + } + + public function getDateTime(): \DateTimeImmutable + { + return $this->dateTime; + } + + /** + * Set the date-time of the Date in this Header. + * + * If a DateTime instance is provided, it is converted to DateTimeImmutable. + */ + public function setDateTime(\DateTimeInterface $dateTime) + { + if ($dateTime instanceof \DateTime) { + $immutable = new \DateTimeImmutable('@'.$dateTime->getTimestamp()); + $dateTime = $immutable->setTimezone($dateTime->getTimezone()); + } + $this->dateTime = $dateTime; + } + + public function getBodyAsString(): string + { + return $this->dateTime->format(\DateTime::RFC2822); + } +} diff --git a/vendor/symfony/mime/Header/HeaderInterface.php b/vendor/symfony/mime/Header/HeaderInterface.php new file mode 100644 index 0000000..4546947 --- /dev/null +++ b/vendor/symfony/mime/Header/HeaderInterface.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +/** + * A MIME Header. + * + * @author Chris Corbyn + */ +interface HeaderInterface +{ + /** + * Sets the body. + * + * The type depends on the Header concrete class. + * + * @param mixed $body + */ + public function setBody($body); + + /** + * Gets the body. + * + * The return type depends on the Header concrete class. + * + * @return mixed + */ + public function getBody(); + + public function setCharset(string $charset); + + public function getCharset(): ?string; + + public function setLanguage(string $lang); + + public function getLanguage(): ?string; + + public function getName(): string; + + public function setMaxLineLength(int $lineLength); + + public function getMaxLineLength(): int; + + /** + * Gets this Header rendered as a compliant string. + */ + public function toString(): string; + + /** + * Gets the header's body, prepared for folding into a final header value. + * + * This is not necessarily RFC 2822 compliant since folding white space is + * not added at this stage (see {@link toString()} for that). + */ + public function getBodyAsString(): string; +} diff --git a/vendor/symfony/mime/Header/Headers.php b/vendor/symfony/mime/Header/Headers.php new file mode 100644 index 0000000..9de506e --- /dev/null +++ b/vendor/symfony/mime/Header/Headers.php @@ -0,0 +1,282 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Exception\LogicException; + +/** + * A collection of headers. + * + * @author Fabien Potencier + */ +final class Headers +{ + private static $uniqueHeaders = [ + 'date', 'from', 'sender', 'reply-to', 'to', 'cc', 'bcc', + 'message-id', 'in-reply-to', 'references', 'subject', + ]; + + private $headers = []; + private $lineLength = 76; + + public function __construct(HeaderInterface ...$headers) + { + foreach ($headers as $header) { + $this->add($header); + } + } + + public function __clone() + { + foreach ($this->headers as $name => $collection) { + foreach ($collection as $i => $header) { + $this->headers[$name][$i] = clone $header; + } + } + } + + public function setMaxLineLength(int $lineLength) + { + $this->lineLength = $lineLength; + foreach ($this->all() as $header) { + $header->setMaxLineLength($lineLength); + } + } + + public function getMaxLineLength(): int + { + return $this->lineLength; + } + + /** + * @param (Address|string)[] $addresses + * + * @return $this + */ + public function addMailboxListHeader(string $name, array $addresses): self + { + return $this->add(new MailboxListHeader($name, Address::createArray($addresses))); + } + + /** + * @param Address|string $address + * + * @return $this + */ + public function addMailboxHeader(string $name, $address): self + { + return $this->add(new MailboxHeader($name, Address::create($address))); + } + + /** + * @param string|array $ids + * + * @return $this + */ + public function addIdHeader(string $name, $ids): self + { + return $this->add(new IdentificationHeader($name, $ids)); + } + + /** + * @param Address|string $path + * + * @return $this + */ + public function addPathHeader(string $name, $path): self + { + return $this->add(new PathHeader($name, $path instanceof Address ? $path : new Address($path))); + } + + /** + * @return $this + */ + public function addDateHeader(string $name, \DateTimeInterface $dateTime): self + { + return $this->add(new DateHeader($name, $dateTime)); + } + + /** + * @return $this + */ + public function addTextHeader(string $name, string $value): self + { + return $this->add(new UnstructuredHeader($name, $value)); + } + + /** + * @return $this + */ + public function addParameterizedHeader(string $name, string $value, array $params = []): self + { + return $this->add(new ParameterizedHeader($name, $value, $params)); + } + + public function has(string $name): bool + { + return isset($this->headers[strtolower($name)]); + } + + /** + * @return $this + */ + public function add(HeaderInterface $header): self + { + static $map = [ + 'date' => DateHeader::class, + 'from' => MailboxListHeader::class, + 'sender' => MailboxHeader::class, + 'reply-to' => MailboxListHeader::class, + 'to' => MailboxListHeader::class, + 'cc' => MailboxListHeader::class, + 'bcc' => MailboxListHeader::class, + 'message-id' => IdentificationHeader::class, + 'in-reply-to' => IdentificationHeader::class, + 'references' => IdentificationHeader::class, + 'return-path' => PathHeader::class, + ]; + + $header->setMaxLineLength($this->lineLength); + $name = strtolower($header->getName()); + + if (isset($map[$name]) && !$header instanceof $map[$name]) { + throw new LogicException(sprintf('The "%s" header must be an instance of "%s" (got "%s").', $header->getName(), $map[$name], \get_class($header))); + } + + if (\in_array($name, self::$uniqueHeaders, true) && isset($this->headers[$name]) && \count($this->headers[$name]) > 0) { + throw new LogicException(sprintf('Impossible to set header "%s" as it\'s already defined and must be unique.', $header->getName())); + } + + $this->headers[$name][] = $header; + + return $this; + } + + public function get(string $name): ?HeaderInterface + { + $name = strtolower($name); + if (!isset($this->headers[$name])) { + return null; + } + + $values = array_values($this->headers[$name]); + + return array_shift($values); + } + + public function all(string $name = null): iterable + { + if (null === $name) { + foreach ($this->headers as $name => $collection) { + foreach ($collection as $header) { + yield $name => $header; + } + } + } elseif (isset($this->headers[strtolower($name)])) { + foreach ($this->headers[strtolower($name)] as $header) { + yield $header; + } + } + } + + public function getNames(): array + { + return array_keys($this->headers); + } + + public function remove(string $name): void + { + unset($this->headers[strtolower($name)]); + } + + public static function isUniqueHeader(string $name): bool + { + return \in_array($name, self::$uniqueHeaders, true); + } + + public function toString(): string + { + $string = ''; + foreach ($this->toArray() as $str) { + $string .= $str."\r\n"; + } + + return $string; + } + + public function toArray(): array + { + $arr = []; + foreach ($this->all() as $header) { + if ('' !== $header->getBodyAsString()) { + $arr[] = $header->toString(); + } + } + + return $arr; + } + + /** + * @internal + */ + public function getHeaderBody($name) + { + return $this->has($name) ? $this->get($name)->getBody() : null; + } + + /** + * @internal + */ + public function setHeaderBody(string $type, string $name, $body): void + { + if ($this->has($name)) { + $this->get($name)->setBody($body); + } else { + $this->{'add'.$type.'Header'}($name, $body); + } + } + + /** + * @internal + */ + public function getHeaderParameter(string $name, string $parameter): ?string + { + if (!$this->has($name)) { + return null; + } + + $header = $this->get($name); + if (!$header instanceof ParameterizedHeader) { + throw new LogicException(sprintf('Unable to get parameter "%s" on header "%s" as the header is not of class "%s".', $parameter, $name, ParameterizedHeader::class)); + } + + return $header->getParameter($parameter); + } + + /** + * @internal + */ + public function setHeaderParameter(string $name, string $parameter, $value): void + { + if (!$this->has($name)) { + throw new LogicException(sprintf('Unable to set parameter "%s" on header "%s" as the header is not defined.', $parameter, $name)); + } + + $header = $this->get($name); + if (!$header instanceof ParameterizedHeader) { + throw new LogicException(sprintf('Unable to set parameter "%s" on header "%s" as the header is not of class "%s".', $parameter, $name, ParameterizedHeader::class)); + } + + $header->setParameter($parameter, $value); + } +} diff --git a/vendor/symfony/mime/Header/IdentificationHeader.php b/vendor/symfony/mime/Header/IdentificationHeader.php new file mode 100644 index 0000000..8a94574 --- /dev/null +++ b/vendor/symfony/mime/Header/IdentificationHeader.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Exception\RfcComplianceException; + +/** + * An ID MIME Header for something like Message-ID or Content-ID (one or more addresses). + * + * @author Chris Corbyn + */ +final class IdentificationHeader extends AbstractHeader +{ + private $ids = []; + private $idsAsAddresses = []; + + /** + * @param string|array $ids + */ + public function __construct(string $name, $ids) + { + parent::__construct($name); + + $this->setId($ids); + } + + /** + * @param string|array $body a string ID or an array of IDs + * + * @throws RfcComplianceException + */ + public function setBody($body) + { + $this->setId($body); + } + + public function getBody(): array + { + return $this->getIds(); + } + + /** + * Set the ID used in the value of this header. + * + * @param string|array $id + * + * @throws RfcComplianceException + */ + public function setId($id) + { + $this->setIds(\is_array($id) ? $id : [$id]); + } + + /** + * Get the ID used in the value of this Header. + * + * If multiple IDs are set only the first is returned. + */ + public function getId(): ?string + { + return $this->ids[0] ?? null; + } + + /** + * Set a collection of IDs to use in the value of this Header. + * + * @param string[] $ids + * + * @throws RfcComplianceException + */ + public function setIds(array $ids) + { + $this->ids = []; + $this->idsAsAddresses = []; + foreach ($ids as $id) { + $this->idsAsAddresses[] = new Address($id); + $this->ids[] = $id; + } + } + + /** + * Get the list of IDs used in this Header. + * + * @return string[] + */ + public function getIds(): array + { + return $this->ids; + } + + public function getBodyAsString(): string + { + $addrs = []; + foreach ($this->idsAsAddresses as $address) { + $addrs[] = '<'.$address->toString().'>'; + } + + return implode(' ', $addrs); + } +} diff --git a/vendor/symfony/mime/Header/MailboxHeader.php b/vendor/symfony/mime/Header/MailboxHeader.php new file mode 100644 index 0000000..b58c825 --- /dev/null +++ b/vendor/symfony/mime/Header/MailboxHeader.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Exception\RfcComplianceException; + +/** + * A Mailbox MIME Header for something like Sender (one named address). + * + * @author Fabien Potencier + */ +final class MailboxHeader extends AbstractHeader +{ + private $address; + + public function __construct(string $name, Address $address) + { + parent::__construct($name); + + $this->setAddress($address); + } + + /** + * @param Address $body + * + * @throws RfcComplianceException + */ + public function setBody($body) + { + $this->setAddress($body); + } + + /** + * @throws RfcComplianceException + */ + public function getBody(): Address + { + return $this->getAddress(); + } + + /** + * @throws RfcComplianceException + */ + public function setAddress(Address $address) + { + $this->address = $address; + } + + public function getAddress(): Address + { + return $this->address; + } + + public function getBodyAsString(): string + { + $str = $this->address->getEncodedAddress(); + if ($name = $this->address->getName()) { + $str = $this->createPhrase($this, $name, $this->getCharset(), true).' <'.$str.'>'; + } + + return $str; + } + + /** + * Redefine the encoding requirements for an address. + * + * All "specials" must be encoded as the full header value will not be quoted + * + * @see RFC 2822 3.2.1 + */ + protected function tokenNeedsEncoding(string $token): bool + { + return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token); + } +} diff --git a/vendor/symfony/mime/Header/MailboxListHeader.php b/vendor/symfony/mime/Header/MailboxListHeader.php new file mode 100644 index 0000000..1d00fdb --- /dev/null +++ b/vendor/symfony/mime/Header/MailboxListHeader.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Exception\RfcComplianceException; + +/** + * A Mailbox list MIME Header for something like From, To, Cc, and Bcc (one or more named addresses). + * + * @author Chris Corbyn + */ +final class MailboxListHeader extends AbstractHeader +{ + private $addresses = []; + + /** + * @param Address[] $addresses + */ + public function __construct(string $name, array $addresses) + { + parent::__construct($name); + + $this->setAddresses($addresses); + } + + /** + * @param Address[] $body + * + * @throws RfcComplianceException + */ + public function setBody($body) + { + $this->setAddresses($body); + } + + /** + * @throws RfcComplianceException + * + * @return Address[] + */ + public function getBody(): array + { + return $this->getAddresses(); + } + + /** + * Sets a list of addresses to be shown in this Header. + * + * @param Address[] $addresses + * + * @throws RfcComplianceException + */ + public function setAddresses(array $addresses) + { + $this->addresses = []; + $this->addAddresses($addresses); + } + + /** + * Sets a list of addresses to be shown in this Header. + * + * @param Address[] $addresses + * + * @throws RfcComplianceException + */ + public function addAddresses(array $addresses) + { + foreach ($addresses as $address) { + $this->addAddress($address); + } + } + + /** + * @throws RfcComplianceException + */ + public function addAddress(Address $address) + { + $this->addresses[] = $address; + } + + /** + * @return Address[] + */ + public function getAddresses(): array + { + return $this->addresses; + } + + /** + * Gets the full mailbox list of this Header as an array of valid RFC 2822 strings. + * + * @throws RfcComplianceException + * + * @return string[] + */ + public function getAddressStrings(): array + { + $strings = []; + foreach ($this->addresses as $address) { + $str = $address->getEncodedAddress(); + if ($name = $address->getName()) { + $str = $this->createPhrase($this, $name, $this->getCharset(), !$strings).' <'.$str.'>'; + } + $strings[] = $str; + } + + return $strings; + } + + public function getBodyAsString(): string + { + return implode(', ', $this->getAddressStrings()); + } + + /** + * Redefine the encoding requirements for addresses. + * + * All "specials" must be encoded as the full header value will not be quoted + * + * @see RFC 2822 3.2.1 + */ + protected function tokenNeedsEncoding(string $token): bool + { + return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token); + } +} diff --git a/vendor/symfony/mime/Header/ParameterizedHeader.php b/vendor/symfony/mime/Header/ParameterizedHeader.php new file mode 100644 index 0000000..d8e5001 --- /dev/null +++ b/vendor/symfony/mime/Header/ParameterizedHeader.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Encoder\Rfc2231Encoder; + +/** + * @author Chris Corbyn + */ +final class ParameterizedHeader extends UnstructuredHeader +{ + /** + * RFC 2231's definition of a token. + * + * @var string + */ + const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)'; + + private $encoder; + private $parameters = []; + + public function __construct(string $name, string $value, array $parameters = []) + { + parent::__construct($name, $value); + + foreach ($parameters as $k => $v) { + $this->setParameter($k, $v); + } + + if ('content-type' !== strtolower($name)) { + $this->encoder = new Rfc2231Encoder(); + } + } + + public function setParameter(string $parameter, ?string $value) + { + $this->setParameters(array_merge($this->getParameters(), [$parameter => $value])); + } + + public function getParameter(string $parameter): string + { + return $this->getParameters()[$parameter] ?? ''; + } + + /** + * @param string[] $parameters + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + } + + /** + * @return string[] + */ + public function getParameters(): array + { + return $this->parameters; + } + + public function getBodyAsString(): string + { + $body = parent::getBodyAsString(); + foreach ($this->parameters as $name => $value) { + if (null !== $value) { + $body .= '; '.$this->createParameter($name, $value); + } + } + + return $body; + } + + /** + * Generate a list of all tokens in the final header. + * + * This doesn't need to be overridden in theory, but it is for implementation + * reasons to prevent potential breakage of attributes. + */ + protected function toTokens(string $string = null): array + { + $tokens = parent::toTokens(parent::getBodyAsString()); + + // Try creating any parameters + foreach ($this->parameters as $name => $value) { + if (null !== $value) { + // Add the semi-colon separator + $tokens[\count($tokens) - 1] .= ';'; + $tokens = array_merge($tokens, $this->generateTokenLines(' '.$this->createParameter($name, $value))); + } + } + + return $tokens; + } + + /** + * Render a RFC 2047 compliant header parameter from the $name and $value. + */ + private function createParameter(string $name, string $value): string + { + $origValue = $value; + + $encoded = false; + // Allow room for parameter name, indices, "=" and DQUOTEs + $maxValueLength = $this->getMaxLineLength() - \strlen($name.'=*N"";') - 1; + $firstLineOffset = 0; + + // If it's not already a valid parameter value... + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + // TODO: text, or something else?? + // ... and it's not ascii + if (!preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $value)) { + $encoded = true; + // Allow space for the indices, charset and language + $maxValueLength = $this->getMaxLineLength() - \strlen($name.'*N*="";') - 1; + $firstLineOffset = \strlen($this->getCharset()."'".$this->getLanguage()."'"); + } + } + + // Encode if we need to + if ($encoded || \strlen($value) > $maxValueLength) { + if (null !== $this->encoder) { + $value = $this->encoder->encodeString($origValue, $this->getCharset(), $firstLineOffset, $maxValueLength); + } else { + // We have to go against RFC 2183/2231 in some areas for interoperability + $value = $this->getTokenAsEncodedWord($origValue); + $encoded = false; + } + } + + $valueLines = $this->encoder ? explode("\r\n", $value) : [$value]; + + // Need to add indices + if (\count($valueLines) > 1) { + $paramLines = []; + foreach ($valueLines as $i => $line) { + $paramLines[] = $name.'*'.$i.$this->getEndOfParameterValue($line, true, 0 === $i); + } + + return implode(";\r\n ", $paramLines); + } else { + return $name.$this->getEndOfParameterValue($valueLines[0], $encoded, true); + } + } + + /** + * Returns the parameter value from the "=" and beyond. + * + * @param string $value to append + */ + private function getEndOfParameterValue(string $value, bool $encoded = false, bool $firstLine = false): string + { + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + $value = '"'.$value.'"'; + } + $prepend = '='; + if ($encoded) { + $prepend = '*='; + if ($firstLine) { + $prepend = '*='.$this->getCharset()."'".$this->getLanguage()."'"; + } + } + + return $prepend.$value; + } +} diff --git a/vendor/symfony/mime/Header/PathHeader.php b/vendor/symfony/mime/Header/PathHeader.php new file mode 100644 index 0000000..5101ad0 --- /dev/null +++ b/vendor/symfony/mime/Header/PathHeader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Exception\RfcComplianceException; + +/** + * A Path Header, such a Return-Path (one address). + * + * @author Chris Corbyn + */ +final class PathHeader extends AbstractHeader +{ + private $address; + + public function __construct(string $name, Address $address) + { + parent::__construct($name); + + $this->setAddress($address); + } + + /** + * @param Address $body + * + * @throws RfcComplianceException + */ + public function setBody($body) + { + $this->setAddress($body); + } + + public function getBody(): Address + { + return $this->getAddress(); + } + + public function setAddress(Address $address) + { + $this->address = $address; + } + + public function getAddress(): Address + { + return $this->address; + } + + public function getBodyAsString(): string + { + return '<'.$this->address->toString().'>'; + } +} diff --git a/vendor/symfony/mime/Header/UnstructuredHeader.php b/vendor/symfony/mime/Header/UnstructuredHeader.php new file mode 100644 index 0000000..2085ddf --- /dev/null +++ b/vendor/symfony/mime/Header/UnstructuredHeader.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Header; + +/** + * A Simple MIME Header. + * + * @author Chris Corbyn + */ +class UnstructuredHeader extends AbstractHeader +{ + private $value; + + public function __construct(string $name, string $value) + { + parent::__construct($name); + + $this->setValue($value); + } + + /** + * @param string $body + */ + public function setBody($body) + { + $this->setValue($body); + } + + /** + * @return string + */ + public function getBody() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + */ + public function getValue(): string + { + return $this->value; + } + + /** + * Set the (unencoded) value of this header. + */ + public function setValue(string $value) + { + $this->value = $value; + } + + /** + * Get the value of this header prepared for rendering. + */ + public function getBodyAsString(): string + { + return $this->encodeWords($this, $this->value); + } +} diff --git a/vendor/symfony/polyfill-php70/LICENSE b/vendor/symfony/mime/LICENSE similarity index 96% rename from vendor/symfony/polyfill-php70/LICENSE rename to vendor/symfony/mime/LICENSE index 24fa32c..d53be68 100644 --- a/vendor/symfony/polyfill-php70/LICENSE +++ b/vendor/symfony/mime/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2018 Fabien Potencier +Copyright (c) 2010-2020 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/symfony/mime/Message.php b/vendor/symfony/mime/Message.php new file mode 100644 index 0000000..5b4e67f --- /dev/null +++ b/vendor/symfony/mime/Message.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\LogicException; +use Symfony\Component\Mime\Header\Headers; +use Symfony\Component\Mime\Part\AbstractPart; +use Symfony\Component\Mime\Part\TextPart; + +/** + * @author Fabien Potencier + */ +class Message extends RawMessage +{ + private $headers; + private $body; + + public function __construct(Headers $headers = null, AbstractPart $body = null) + { + $this->headers = $headers ? clone $headers : new Headers(); + $this->body = $body; + } + + public function __clone() + { + $this->headers = clone $this->headers; + + if (null !== $this->body) { + $this->body = clone $this->body; + } + } + + /** + * @return $this + */ + public function setBody(AbstractPart $body = null) + { + $this->body = $body; + + return $this; + } + + public function getBody(): ?AbstractPart + { + return $this->body; + } + + /** + * @return $this + */ + public function setHeaders(Headers $headers) + { + $this->headers = $headers; + + return $this; + } + + public function getHeaders(): Headers + { + return $this->headers; + } + + public function getPreparedHeaders(): Headers + { + $headers = clone $this->headers; + + if (!$headers->has('From')) { + throw new LogicException('An email must have a "From" header.'); + } + + $headers->addTextHeader('MIME-Version', '1.0'); + + if (!$headers->has('Date')) { + $headers->addDateHeader('Date', new \DateTimeImmutable()); + } + + // determine the "real" sender + if (!$headers->has('Sender') && \count($froms = $headers->get('From')->getAddresses()) > 1) { + $headers->addMailboxHeader('Sender', $froms[0]); + } + + if (!$headers->has('Message-ID')) { + $headers->addIdHeader('Message-ID', $this->generateMessageId()); + } + + // remove the Bcc field which should NOT be part of the sent message + $headers->remove('Bcc'); + + return $headers; + } + + public function toString(): string + { + if (null === $body = $this->getBody()) { + $body = new TextPart(''); + } + + return $this->getPreparedHeaders()->toString().$body->toString(); + } + + public function toIterable(): iterable + { + if (null === $body = $this->getBody()) { + $body = new TextPart(''); + } + + yield $this->getPreparedHeaders()->toString(); + yield from $body->toIterable(); + } + + public function ensureValidity() + { + if (!$this->headers->has('From')) { + throw new LogicException('An email must have a "From" header.'); + } + + parent::ensureValidity(); + } + + public function generateMessageId(): string + { + if ($this->headers->has('Sender')) { + $sender = $this->headers->get('Sender')->getAddress(); + } elseif ($this->headers->has('From')) { + $sender = $this->headers->get('From')->getAddresses()[0]; + } else { + throw new LogicException('An email must have a "From" or a "Sender" header to compute a Messsage ID.'); + } + + return bin2hex(random_bytes(16)).strstr($sender->getAddress(), '@'); + } + + public function __serialize(): array + { + return [$this->headers, $this->body]; + } + + public function __unserialize(array $data): void + { + [$this->headers, $this->body] = $data; + } +} diff --git a/vendor/symfony/mime/MessageConverter.php b/vendor/symfony/mime/MessageConverter.php new file mode 100644 index 0000000..a810cb7 --- /dev/null +++ b/vendor/symfony/mime/MessageConverter.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\RuntimeException; +use Symfony\Component\Mime\Part\DataPart; +use Symfony\Component\Mime\Part\Multipart\AlternativePart; +use Symfony\Component\Mime\Part\Multipart\MixedPart; +use Symfony\Component\Mime\Part\Multipart\RelatedPart; +use Symfony\Component\Mime\Part\TextPart; + +/** + * @author Fabien Potencier + */ +final class MessageConverter +{ + /** + * @throws RuntimeException when unable to convert the message to an email + */ + public static function toEmail(Message $message): Email + { + if ($message instanceof Email) { + return $message; + } + + // try to convert to a "simple" Email instance + $body = $message->getBody(); + if ($body instanceof TextPart) { + return self::createEmailFromTextPart($message, $body); + } + + if ($body instanceof AlternativePart) { + return self::createEmailFromAlternativePart($message, $body); + } + + if ($body instanceof RelatedPart) { + return self::createEmailFromRelatedPart($message, $body); + } + + if ($body instanceof MixedPart) { + $parts = $body->getParts(); + if ($parts[0] instanceof RelatedPart) { + $email = self::createEmailFromRelatedPart($message, $parts[0]); + } elseif ($parts[0] instanceof AlternativePart) { + $email = self::createEmailFromAlternativePart($message, $parts[0]); + } elseif ($parts[0] instanceof TextPart) { + $email = self::createEmailFromTextPart($message, $parts[0]); + } else { + throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message))); + } + + return self::attachParts($email, \array_slice($parts, 1)); + } + + throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message))); + } + + private static function createEmailFromTextPart(Message $message, TextPart $part): Email + { + if ('text' === $part->getMediaType() && 'plain' === $part->getMediaSubtype()) { + return (new Email(clone $message->getHeaders()))->text($part->getBody(), $part->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8'); + } + if ('text' === $part->getMediaType() && 'html' === $part->getMediaSubtype()) { + return (new Email(clone $message->getHeaders()))->html($part->getBody(), $part->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8'); + } + + throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message))); + } + + private static function createEmailFromAlternativePart(Message $message, AlternativePart $part): Email + { + $parts = $part->getParts(); + if ( + 2 === \count($parts) && + $parts[0] instanceof TextPart && 'text' === $parts[0]->getMediaType() && 'plain' === $parts[0]->getMediaSubtype() && + $parts[1] instanceof TextPart && 'text' === $parts[1]->getMediaType() && 'html' === $parts[1]->getMediaSubtype() + ) { + return (new Email(clone $message->getHeaders())) + ->text($parts[0]->getBody(), $parts[0]->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8') + ->html($parts[1]->getBody(), $parts[1]->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8') + ; + } + + throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message))); + } + + private static function createEmailFromRelatedPart(Message $message, RelatedPart $part): Email + { + $parts = $part->getParts(); + if ($parts[0] instanceof AlternativePart) { + $email = self::createEmailFromAlternativePart($message, $parts[0]); + } elseif ($parts[0] instanceof TextPart) { + $email = self::createEmailFromTextPart($message, $parts[0]); + } else { + throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message))); + } + + return self::attachParts($email, \array_slice($parts, 1)); + } + + private static function attachParts(Email $email, array $parts): Email + { + foreach ($parts as $part) { + if (!$part instanceof DataPart) { + throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($email))); + } + + $headers = $part->getPreparedHeaders(); + $method = 'inline' === $headers->getHeaderBody('Content-Disposition') ? 'embed' : 'attach'; + $name = $headers->getHeaderParameter('Content-Disposition', 'filename'); + $email->$method($part->getBody(), $name, $part->getMediaType().'/'.$part->getMediaSubtype()); + } + + return $email; + } +} diff --git a/vendor/symfony/mime/MimeTypeGuesserInterface.php b/vendor/symfony/mime/MimeTypeGuesserInterface.php new file mode 100644 index 0000000..68b0505 --- /dev/null +++ b/vendor/symfony/mime/MimeTypeGuesserInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +/** + * Guesses the MIME type of a file. + * + * @author Fabien Potencier + */ +interface MimeTypeGuesserInterface +{ + /** + * Returns true if this guesser is supported. + */ + public function isGuesserSupported(): bool; + + /** + * Guesses the MIME type of the file with the given path. + * + * @param string $path The path to the file + * + * @return string|null The MIME type or null, if none could be guessed + * + * @throws \LogicException If the guesser is not supported + * @throws \InvalidArgumentException If the file does not exist or is not readable + */ + public function guessMimeType(string $path): ?string; +} diff --git a/vendor/symfony/mime/MimeTypes.php b/vendor/symfony/mime/MimeTypes.php new file mode 100644 index 0000000..268658d --- /dev/null +++ b/vendor/symfony/mime/MimeTypes.php @@ -0,0 +1,3153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\LogicException; + +/** + * Manages MIME types and file extensions. + * + * For MIME type guessing, you can register custom guessers + * by calling the registerGuesser() method. + * Custom guessers are always called before any default ones: + * + * $guesser = new MimeTypes(); + * $guesser->registerGuesser(new MyCustomMimeTypeGuesser()); + * + * If you want to change the order of the default guessers, just re-register your + * preferred one as a custom one. The last registered guesser is preferred over + * previously registered ones. + * + * Re-registering a built-in guesser also allows you to configure it: + * + * $guesser = new MimeTypes(); + * $guesser->registerGuesser(new FileinfoMimeTypeGuesser('/path/to/magic/file')); + * + * @author Fabien Potencier + */ +final class MimeTypes implements MimeTypesInterface +{ + private $extensions = []; + private $mimeTypes = []; + + /** + * @var MimeTypeGuesserInterface[] + */ + private $guessers = []; + private static $default; + + public function __construct(array $map = []) + { + foreach ($map as $mimeType => $extensions) { + $this->extensions[$mimeType] = $extensions; + + foreach ($extensions as $extension) { + $this->mimeTypes[$extension] = $mimeType; + } + } + $this->registerGuesser(new FileBinaryMimeTypeGuesser()); + $this->registerGuesser(new FileinfoMimeTypeGuesser()); + } + + public static function setDefault(self $default) + { + self::$default = $default; + } + + public static function getDefault(): self + { + return self::$default ?? self::$default = new self(); + } + + /** + * Registers a MIME type guesser. + * + * The last registered guesser has precedence over the other ones. + */ + public function registerGuesser(MimeTypeGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * {@inheritdoc} + */ + public function getExtensions(string $mimeType): array + { + if ($this->extensions) { + $extensions = $this->extensions[$mimeType] ?? $this->extensions[$lcMimeType = strtolower($mimeType)] ?? null; + } + + return $extensions ?? self::$map[$mimeType] ?? self::$map[$lcMimeType ?? strtolower($mimeType)] ?? []; + } + + /** + * {@inheritdoc} + */ + public function getMimeTypes(string $ext): array + { + if ($this->mimeTypes) { + $mimeTypes = $this->mimeTypes[$ext] ?? $this->mimeTypes[$lcExt = strtolower($ext)] ?? null; + } + + return $mimeTypes ?? self::$reverseMap[$ext] ?? self::$reverseMap[$lcExt ?? strtolower($ext)] ?? []; + } + + /** + * {@inheritdoc} + */ + public function isGuesserSupported(): bool + { + foreach ($this->guessers as $guesser) { + if ($guesser->isGuesserSupported()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + * + * The file is passed to each registered MIME type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not null, this method terminates and returns the + * value. + */ + public function guessMimeType(string $path): ?string + { + foreach ($this->guessers as $guesser) { + if (!$guesser->isGuesserSupported()) { + continue; + } + + if (null !== $mimeType = $guesser->guessMimeType($path)) { + return $mimeType; + } + } + + if (!$this->isGuesserSupported()) { + throw new LogicException('Unable to guess the MIME type as no guessers are available (have you enable the php_fileinfo extension?).'); + } + + return null; + } + + /** + * A map of MIME types and their default extensions. + * + * Updated from upstream on 2019-01-16 + * + * @see Resources/bin/update_mime_types.php + */ + private static $map = [ + 'application/acrobat' => ['pdf'], + 'application/andrew-inset' => ['ez'], + 'application/annodex' => ['anx'], + 'application/applixware' => ['aw'], + 'application/atom+xml' => ['atom'], + 'application/atomcat+xml' => ['atomcat'], + 'application/atomsvc+xml' => ['atomsvc'], + 'application/ccxml+xml' => ['ccxml'], + 'application/cdmi-capability' => ['cdmia'], + 'application/cdmi-container' => ['cdmic'], + 'application/cdmi-domain' => ['cdmid'], + 'application/cdmi-object' => ['cdmio'], + 'application/cdmi-queue' => ['cdmiq'], + 'application/cdr' => ['cdr'], + 'application/coreldraw' => ['cdr'], + 'application/cu-seeme' => ['cu'], + 'application/davmount+xml' => ['davmount'], + 'application/dbase' => ['dbf'], + 'application/dbf' => ['dbf'], + 'application/dicom' => ['dcm'], + 'application/docbook+xml' => ['dbk', 'docbook'], + 'application/dssc+der' => ['dssc'], + 'application/dssc+xml' => ['xdssc'], + 'application/ecmascript' => ['ecma', 'es'], + 'application/emf' => ['emf'], + 'application/emma+xml' => ['emma'], + 'application/epub+zip' => ['epub'], + 'application/exi' => ['exi'], + 'application/font-tdpfr' => ['pfr'], + 'application/font-woff' => ['woff'], + 'application/futuresplash' => ['swf', 'spl'], + 'application/geo+json' => ['geojson', 'geo.json'], + 'application/gml+xml' => ['gml'], + 'application/gnunet-directory' => ['gnd'], + 'application/gpx' => ['gpx'], + 'application/gpx+xml' => ['gpx'], + 'application/gxf' => ['gxf'], + 'application/gzip' => ['gz'], + 'application/hyperstudio' => ['stk'], + 'application/ico' => ['ico'], + 'application/ics' => ['vcs', 'ics'], + 'application/illustrator' => ['ai'], + 'application/inkml+xml' => ['ink', 'inkml'], + 'application/ipfix' => ['ipfix'], + 'application/java' => ['class'], + 'application/java-archive' => ['jar'], + 'application/java-byte-code' => ['class'], + 'application/java-serialized-object' => ['ser'], + 'application/java-vm' => ['class'], + 'application/javascript' => ['js', 'jsm', 'mjs'], + 'application/jrd+json' => ['jrd'], + 'application/json' => ['json'], + 'application/json-patch+json' => ['json-patch'], + 'application/jsonml+json' => ['jsonml'], + 'application/ld+json' => ['jsonld'], + 'application/lost+xml' => ['lostxml'], + 'application/lotus123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'], + 'application/m3u' => ['m3u', 'm3u8', 'vlc'], + 'application/mac-binhex40' => ['hqx'], + 'application/mac-compactpro' => ['cpt'], + 'application/mads+xml' => ['mads'], + 'application/marc' => ['mrc'], + 'application/marcxml+xml' => ['mrcx'], + 'application/mathematica' => ['ma', 'nb', 'mb'], + 'application/mathml+xml' => ['mathml', 'mml'], + 'application/mbox' => ['mbox'], + 'application/mdb' => ['mdb'], + 'application/mediaservercontrol+xml' => ['mscml'], + 'application/metalink+xml' => ['metalink'], + 'application/metalink4+xml' => ['meta4'], + 'application/mets+xml' => ['mets'], + 'application/mods+xml' => ['mods'], + 'application/mp21' => ['m21', 'mp21'], + 'application/mp4' => ['mp4s'], + 'application/ms-tnef' => ['tnef', 'tnf'], + 'application/msaccess' => ['mdb'], + 'application/msexcel' => ['xls', 'xlc', 'xll', 'xlm', 'xlw', 'xla', 'xlt', 'xld'], + 'application/mspowerpoint' => ['ppz', 'ppt', 'pps', 'pot'], + 'application/msword' => ['doc', 'dot'], + 'application/msword-template' => ['dot'], + 'application/mxf' => ['mxf'], + 'application/nappdf' => ['pdf'], + 'application/octet-stream' => ['bin', 'dms', 'lrf', 'mar', 'so', 'dist', 'distz', 'pkg', 'bpk', 'dump', 'elc', 'deploy'], + 'application/oda' => ['oda'], + 'application/oebps-package+xml' => ['opf'], + 'application/ogg' => ['ogx'], + 'application/omdoc+xml' => ['omdoc'], + 'application/onenote' => ['onetoc', 'onetoc2', 'onetmp', 'onepkg'], + 'application/owl+xml' => ['owx'], + 'application/oxps' => ['oxps', 'xps'], + 'application/patch-ops-error+xml' => ['xer'], + 'application/pcap' => ['pcap', 'cap', 'dmp'], + 'application/pdf' => ['pdf'], + 'application/pgp' => ['pgp', 'gpg', 'asc'], + 'application/pgp-encrypted' => ['pgp', 'gpg', 'asc'], + 'application/pgp-keys' => ['skr', 'pkr', 'asc', 'pgp', 'gpg'], + 'application/pgp-signature' => ['asc', 'sig', 'pgp', 'gpg'], + 'application/photoshop' => ['psd'], + 'application/pics-rules' => ['prf'], + 'application/pkcs10' => ['p10'], + 'application/pkcs12' => ['p12', 'pfx'], + 'application/pkcs7-mime' => ['p7m', 'p7c'], + 'application/pkcs7-signature' => ['p7s'], + 'application/pkcs8' => ['p8'], + 'application/pkcs8-encrypted' => ['p8e'], + 'application/pkix-attr-cert' => ['ac'], + 'application/pkix-cert' => ['cer'], + 'application/pkix-crl' => ['crl'], + 'application/pkix-pkipath' => ['pkipath'], + 'application/pkixcmp' => ['pki'], + 'application/pls' => ['pls'], + 'application/pls+xml' => ['pls'], + 'application/postscript' => ['ai', 'eps', 'ps'], + 'application/powerpoint' => ['ppz', 'ppt', 'pps', 'pot'], + 'application/prs.cww' => ['cww'], + 'application/pskc+xml' => ['pskcxml'], + 'application/ram' => ['ram'], + 'application/raml+yaml' => ['raml'], + 'application/rdf+xml' => ['rdf', 'rdfs', 'owl'], + 'application/reginfo+xml' => ['rif'], + 'application/relax-ng-compact-syntax' => ['rnc'], + 'application/resource-lists+xml' => ['rl'], + 'application/resource-lists-diff+xml' => ['rld'], + 'application/rls-services+xml' => ['rs'], + 'application/rpki-ghostbusters' => ['gbr'], + 'application/rpki-manifest' => ['mft'], + 'application/rpki-roa' => ['roa'], + 'application/rsd+xml' => ['rsd'], + 'application/rss+xml' => ['rss'], + 'application/rtf' => ['rtf'], + 'application/sbml+xml' => ['sbml'], + 'application/scvp-cv-request' => ['scq'], + 'application/scvp-cv-response' => ['scs'], + 'application/scvp-vp-request' => ['spq'], + 'application/scvp-vp-response' => ['spp'], + 'application/sdp' => ['sdp'], + 'application/set-payment-initiation' => ['setpay'], + 'application/set-registration-initiation' => ['setreg'], + 'application/shf+xml' => ['shf'], + 'application/sieve' => ['siv'], + 'application/smil' => ['smil', 'smi', 'sml', 'kino'], + 'application/smil+xml' => ['smi', 'smil', 'sml', 'kino'], + 'application/sparql-query' => ['rq'], + 'application/sparql-results+xml' => ['srx'], + 'application/sql' => ['sql'], + 'application/srgs' => ['gram'], + 'application/srgs+xml' => ['grxml'], + 'application/sru+xml' => ['sru'], + 'application/ssdl+xml' => ['ssdl'], + 'application/ssml+xml' => ['ssml'], + 'application/stuffit' => ['sit'], + 'application/tei+xml' => ['tei', 'teicorpus'], + 'application/thraud+xml' => ['tfi'], + 'application/timestamped-data' => ['tsd'], + 'application/trig' => ['trig'], + 'application/vnd.3gpp.pic-bw-large' => ['plb'], + 'application/vnd.3gpp.pic-bw-small' => ['psb'], + 'application/vnd.3gpp.pic-bw-var' => ['pvb'], + 'application/vnd.3gpp2.tcap' => ['tcap'], + 'application/vnd.3m.post-it-notes' => ['pwn'], + 'application/vnd.accpac.simply.aso' => ['aso'], + 'application/vnd.accpac.simply.imp' => ['imp'], + 'application/vnd.acucobol' => ['acu'], + 'application/vnd.acucorp' => ['atc', 'acutc'], + 'application/vnd.adobe.air-application-installer-package+zip' => ['air'], + 'application/vnd.adobe.flash.movie' => ['swf', 'spl'], + 'application/vnd.adobe.formscentral.fcdt' => ['fcdt'], + 'application/vnd.adobe.fxp' => ['fxp', 'fxpl'], + 'application/vnd.adobe.illustrator' => ['ai'], + 'application/vnd.adobe.xdp+xml' => ['xdp'], + 'application/vnd.adobe.xfdf' => ['xfdf'], + 'application/vnd.ahead.space' => ['ahead'], + 'application/vnd.airzip.filesecure.azf' => ['azf'], + 'application/vnd.airzip.filesecure.azs' => ['azs'], + 'application/vnd.amazon.ebook' => ['azw'], + 'application/vnd.americandynamics.acc' => ['acc'], + 'application/vnd.amiga.ami' => ['ami'], + 'application/vnd.android.package-archive' => ['apk'], + 'application/vnd.anser-web-certificate-issue-initiation' => ['cii'], + 'application/vnd.anser-web-funds-transfer-initiation' => ['fti'], + 'application/vnd.antix.game-component' => ['atx'], + 'application/vnd.appimage' => ['appimage'], + 'application/vnd.apple.installer+xml' => ['mpkg'], + 'application/vnd.apple.keynote' => ['key'], + 'application/vnd.apple.mpegurl' => ['m3u8', 'm3u'], + 'application/vnd.aristanetworks.swi' => ['swi'], + 'application/vnd.astraea-software.iota' => ['iota'], + 'application/vnd.audiograph' => ['aep'], + 'application/vnd.blueice.multipass' => ['mpm'], + 'application/vnd.bmi' => ['bmi'], + 'application/vnd.businessobjects' => ['rep'], + 'application/vnd.chemdraw+xml' => ['cdxml'], + 'application/vnd.chess-pgn' => ['pgn'], + 'application/vnd.chipnuts.karaoke-mmd' => ['mmd'], + 'application/vnd.cinderella' => ['cdy'], + 'application/vnd.claymore' => ['cla'], + 'application/vnd.cloanto.rp9' => ['rp9'], + 'application/vnd.clonk.c4group' => ['c4g', 'c4d', 'c4f', 'c4p', 'c4u'], + 'application/vnd.cluetrust.cartomobile-config' => ['c11amc'], + 'application/vnd.cluetrust.cartomobile-config-pkg' => ['c11amz'], + 'application/vnd.coffeescript' => ['coffee'], + 'application/vnd.comicbook+zip' => ['cbz'], + 'application/vnd.comicbook-rar' => ['cbr'], + 'application/vnd.commonspace' => ['csp'], + 'application/vnd.contact.cmsg' => ['cdbcmsg'], + 'application/vnd.corel-draw' => ['cdr'], + 'application/vnd.cosmocaller' => ['cmc'], + 'application/vnd.crick.clicker' => ['clkx'], + 'application/vnd.crick.clicker.keyboard' => ['clkk'], + 'application/vnd.crick.clicker.palette' => ['clkp'], + 'application/vnd.crick.clicker.template' => ['clkt'], + 'application/vnd.crick.clicker.wordbank' => ['clkw'], + 'application/vnd.criticaltools.wbs+xml' => ['wbs'], + 'application/vnd.ctc-posml' => ['pml'], + 'application/vnd.cups-ppd' => ['ppd'], + 'application/vnd.curl.car' => ['car'], + 'application/vnd.curl.pcurl' => ['pcurl'], + 'application/vnd.dart' => ['dart'], + 'application/vnd.data-vision.rdz' => ['rdz'], + 'application/vnd.debian.binary-package' => ['deb', 'udeb'], + 'application/vnd.dece.data' => ['uvf', 'uvvf', 'uvd', 'uvvd'], + 'application/vnd.dece.ttml+xml' => ['uvt', 'uvvt'], + 'application/vnd.dece.unspecified' => ['uvx', 'uvvx'], + 'application/vnd.dece.zip' => ['uvz', 'uvvz'], + 'application/vnd.denovo.fcselayout-link' => ['fe_launch'], + 'application/vnd.dna' => ['dna'], + 'application/vnd.dolby.mlp' => ['mlp'], + 'application/vnd.dpgraph' => ['dpg'], + 'application/vnd.dreamfactory' => ['dfac'], + 'application/vnd.ds-keypoint' => ['kpxx'], + 'application/vnd.dvb.ait' => ['ait'], + 'application/vnd.dvb.service' => ['svc'], + 'application/vnd.dynageo' => ['geo'], + 'application/vnd.ecowin.chart' => ['mag'], + 'application/vnd.emusic-emusic_package' => ['emp'], + 'application/vnd.enliven' => ['nml'], + 'application/vnd.epson.esf' => ['esf'], + 'application/vnd.epson.msf' => ['msf'], + 'application/vnd.epson.quickanime' => ['qam'], + 'application/vnd.epson.salt' => ['slt'], + 'application/vnd.epson.ssf' => ['ssf'], + 'application/vnd.eszigno3+xml' => ['es3', 'et3'], + 'application/vnd.ezpix-album' => ['ez2'], + 'application/vnd.ezpix-package' => ['ez3'], + 'application/vnd.fdf' => ['fdf'], + 'application/vnd.fdsn.mseed' => ['mseed'], + 'application/vnd.fdsn.seed' => ['seed', 'dataless'], + 'application/vnd.flatpak' => ['flatpak', 'xdgapp'], + 'application/vnd.flatpak.ref' => ['flatpakref'], + 'application/vnd.flatpak.repo' => ['flatpakrepo'], + 'application/vnd.flographit' => ['gph'], + 'application/vnd.fluxtime.clip' => ['ftc'], + 'application/vnd.framemaker' => ['fm', 'frame', 'maker', 'book'], + 'application/vnd.frogans.fnc' => ['fnc'], + 'application/vnd.frogans.ltf' => ['ltf'], + 'application/vnd.fsc.weblaunch' => ['fsc'], + 'application/vnd.fujitsu.oasys' => ['oas'], + 'application/vnd.fujitsu.oasys2' => ['oa2'], + 'application/vnd.fujitsu.oasys3' => ['oa3'], + 'application/vnd.fujitsu.oasysgp' => ['fg5'], + 'application/vnd.fujitsu.oasysprs' => ['bh2'], + 'application/vnd.fujixerox.ddd' => ['ddd'], + 'application/vnd.fujixerox.docuworks' => ['xdw'], + 'application/vnd.fujixerox.docuworks.binder' => ['xbd'], + 'application/vnd.fuzzysheet' => ['fzs'], + 'application/vnd.genomatix.tuxedo' => ['txd'], + 'application/vnd.geo+json' => ['geojson', 'geo.json'], + 'application/vnd.geogebra.file' => ['ggb'], + 'application/vnd.geogebra.tool' => ['ggt'], + 'application/vnd.geometry-explorer' => ['gex', 'gre'], + 'application/vnd.geonext' => ['gxt'], + 'application/vnd.geoplan' => ['g2w'], + 'application/vnd.geospace' => ['g3w'], + 'application/vnd.gmx' => ['gmx'], + 'application/vnd.google-earth.kml+xml' => ['kml'], + 'application/vnd.google-earth.kmz' => ['kmz'], + 'application/vnd.grafeq' => ['gqf', 'gqs'], + 'application/vnd.groove-account' => ['gac'], + 'application/vnd.groove-help' => ['ghf'], + 'application/vnd.groove-identity-message' => ['gim'], + 'application/vnd.groove-injector' => ['grv'], + 'application/vnd.groove-tool-message' => ['gtm'], + 'application/vnd.groove-tool-template' => ['tpl'], + 'application/vnd.groove-vcard' => ['vcg'], + 'application/vnd.haansoft-hwp' => ['hwp'], + 'application/vnd.haansoft-hwt' => ['hwt'], + 'application/vnd.hal+xml' => ['hal'], + 'application/vnd.handheld-entertainment+xml' => ['zmm'], + 'application/vnd.hbci' => ['hbci'], + 'application/vnd.hhe.lesson-player' => ['les'], + 'application/vnd.hp-hpgl' => ['hpgl'], + 'application/vnd.hp-hpid' => ['hpid'], + 'application/vnd.hp-hps' => ['hps'], + 'application/vnd.hp-jlyt' => ['jlt'], + 'application/vnd.hp-pcl' => ['pcl'], + 'application/vnd.hp-pclxl' => ['pclxl'], + 'application/vnd.hydrostatix.sof-data' => ['sfd-hdstx'], + 'application/vnd.ibm.minipay' => ['mpy'], + 'application/vnd.ibm.modcap' => ['afp', 'listafp', 'list3820'], + 'application/vnd.ibm.rights-management' => ['irm'], + 'application/vnd.ibm.secure-container' => ['sc'], + 'application/vnd.iccprofile' => ['icc', 'icm'], + 'application/vnd.igloader' => ['igl'], + 'application/vnd.immervision-ivp' => ['ivp'], + 'application/vnd.immervision-ivu' => ['ivu'], + 'application/vnd.insors.igm' => ['igm'], + 'application/vnd.intercon.formnet' => ['xpw', 'xpx'], + 'application/vnd.intergeo' => ['i2g'], + 'application/vnd.intu.qbo' => ['qbo'], + 'application/vnd.intu.qfx' => ['qfx'], + 'application/vnd.ipunplugged.rcprofile' => ['rcprofile'], + 'application/vnd.irepository.package+xml' => ['irp'], + 'application/vnd.is-xpr' => ['xpr'], + 'application/vnd.isac.fcs' => ['fcs'], + 'application/vnd.jam' => ['jam'], + 'application/vnd.jcp.javame.midlet-rms' => ['rms'], + 'application/vnd.jisp' => ['jisp'], + 'application/vnd.joost.joda-archive' => ['joda'], + 'application/vnd.kahootz' => ['ktz', 'ktr'], + 'application/vnd.kde.karbon' => ['karbon'], + 'application/vnd.kde.kchart' => ['chrt'], + 'application/vnd.kde.kformula' => ['kfo'], + 'application/vnd.kde.kivio' => ['flw'], + 'application/vnd.kde.kontour' => ['kon'], + 'application/vnd.kde.kpresenter' => ['kpr', 'kpt'], + 'application/vnd.kde.kspread' => ['ksp'], + 'application/vnd.kde.kword' => ['kwd', 'kwt'], + 'application/vnd.kenameaapp' => ['htke'], + 'application/vnd.kidspiration' => ['kia'], + 'application/vnd.kinar' => ['kne', 'knp'], + 'application/vnd.koan' => ['skp', 'skd', 'skt', 'skm'], + 'application/vnd.kodak-descriptor' => ['sse'], + 'application/vnd.las.las+xml' => ['lasxml'], + 'application/vnd.llamagraphics.life-balance.desktop' => ['lbd'], + 'application/vnd.llamagraphics.life-balance.exchange+xml' => ['lbe'], + 'application/vnd.lotus-1-2-3' => ['123', 'wk1', 'wk3', 'wk4', 'wks'], + 'application/vnd.lotus-approach' => ['apr'], + 'application/vnd.lotus-freelance' => ['pre'], + 'application/vnd.lotus-notes' => ['nsf'], + 'application/vnd.lotus-organizer' => ['org'], + 'application/vnd.lotus-screencam' => ['scm'], + 'application/vnd.lotus-wordpro' => ['lwp'], + 'application/vnd.macports.portpkg' => ['portpkg'], + 'application/vnd.mcd' => ['mcd'], + 'application/vnd.medcalcdata' => ['mc1'], + 'application/vnd.mediastation.cdkey' => ['cdkey'], + 'application/vnd.mfer' => ['mwf'], + 'application/vnd.mfmp' => ['mfm'], + 'application/vnd.micrografx.flo' => ['flo'], + 'application/vnd.micrografx.igx' => ['igx'], + 'application/vnd.mif' => ['mif'], + 'application/vnd.mobius.daf' => ['daf'], + 'application/vnd.mobius.dis' => ['dis'], + 'application/vnd.mobius.mbk' => ['mbk'], + 'application/vnd.mobius.mqy' => ['mqy'], + 'application/vnd.mobius.msl' => ['msl'], + 'application/vnd.mobius.plc' => ['plc'], + 'application/vnd.mobius.txf' => ['txf'], + 'application/vnd.mophun.application' => ['mpn'], + 'application/vnd.mophun.certificate' => ['mpc'], + 'application/vnd.mozilla.xul+xml' => ['xul'], + 'application/vnd.ms-access' => ['mdb'], + 'application/vnd.ms-artgalry' => ['cil'], + 'application/vnd.ms-asf' => ['asf'], + 'application/vnd.ms-cab-compressed' => ['cab'], + 'application/vnd.ms-excel' => ['xls', 'xlm', 'xla', 'xlc', 'xlt', 'xlw', 'xll', 'xld'], + 'application/vnd.ms-excel.addin.macroenabled.12' => ['xlam'], + 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => ['xlsb'], + 'application/vnd.ms-excel.sheet.macroenabled.12' => ['xlsm'], + 'application/vnd.ms-excel.template.macroenabled.12' => ['xltm'], + 'application/vnd.ms-fontobject' => ['eot'], + 'application/vnd.ms-htmlhelp' => ['chm'], + 'application/vnd.ms-ims' => ['ims'], + 'application/vnd.ms-lrm' => ['lrm'], + 'application/vnd.ms-officetheme' => ['thmx'], + 'application/vnd.ms-pki.seccat' => ['cat'], + 'application/vnd.ms-pki.stl' => ['stl'], + 'application/vnd.ms-powerpoint' => ['ppt', 'pps', 'pot', 'ppz'], + 'application/vnd.ms-powerpoint.addin.macroenabled.12' => ['ppam'], + 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => ['pptm'], + 'application/vnd.ms-powerpoint.slide.macroenabled.12' => ['sldm'], + 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => ['ppsm'], + 'application/vnd.ms-powerpoint.template.macroenabled.12' => ['potm'], + 'application/vnd.ms-project' => ['mpp', 'mpt'], + 'application/vnd.ms-publisher' => ['pub'], + 'application/vnd.ms-tnef' => ['tnef', 'tnf'], + 'application/vnd.ms-visio.drawing.macroenabled.main+xml' => ['vsdm'], + 'application/vnd.ms-visio.drawing.main+xml' => ['vsdx'], + 'application/vnd.ms-visio.stencil.macroenabled.main+xml' => ['vssm'], + 'application/vnd.ms-visio.stencil.main+xml' => ['vssx'], + 'application/vnd.ms-visio.template.macroenabled.main+xml' => ['vstm'], + 'application/vnd.ms-visio.template.main+xml' => ['vstx'], + 'application/vnd.ms-word' => ['doc'], + 'application/vnd.ms-word.document.macroenabled.12' => ['docm'], + 'application/vnd.ms-word.template.macroenabled.12' => ['dotm'], + 'application/vnd.ms-works' => ['wps', 'wks', 'wcm', 'wdb', 'xlr'], + 'application/vnd.ms-wpl' => ['wpl'], + 'application/vnd.ms-xpsdocument' => ['xps', 'oxps'], + 'application/vnd.msaccess' => ['mdb'], + 'application/vnd.mseq' => ['mseq'], + 'application/vnd.musician' => ['mus'], + 'application/vnd.muvee.style' => ['msty'], + 'application/vnd.mynfc' => ['taglet'], + 'application/vnd.neurolanguage.nlu' => ['nlu'], + 'application/vnd.nintendo.snes.rom' => ['sfc', 'smc'], + 'application/vnd.nitf' => ['ntf', 'nitf'], + 'application/vnd.noblenet-directory' => ['nnd'], + 'application/vnd.noblenet-sealer' => ['nns'], + 'application/vnd.noblenet-web' => ['nnw'], + 'application/vnd.nokia.n-gage.data' => ['ngdat'], + 'application/vnd.nokia.n-gage.symbian.install' => ['n-gage'], + 'application/vnd.nokia.radio-preset' => ['rpst'], + 'application/vnd.nokia.radio-presets' => ['rpss'], + 'application/vnd.novadigm.edm' => ['edm'], + 'application/vnd.novadigm.edx' => ['edx'], + 'application/vnd.novadigm.ext' => ['ext'], + 'application/vnd.oasis.docbook+xml' => ['dbk', 'docbook'], + 'application/vnd.oasis.opendocument.chart' => ['odc'], + 'application/vnd.oasis.opendocument.chart-template' => ['otc'], + 'application/vnd.oasis.opendocument.database' => ['odb'], + 'application/vnd.oasis.opendocument.formula' => ['odf'], + 'application/vnd.oasis.opendocument.formula-template' => ['odft', 'otf'], + 'application/vnd.oasis.opendocument.graphics' => ['odg'], + 'application/vnd.oasis.opendocument.graphics-flat-xml' => ['fodg'], + 'application/vnd.oasis.opendocument.graphics-template' => ['otg'], + 'application/vnd.oasis.opendocument.image' => ['odi'], + 'application/vnd.oasis.opendocument.image-template' => ['oti'], + 'application/vnd.oasis.opendocument.presentation' => ['odp'], + 'application/vnd.oasis.opendocument.presentation-flat-xml' => ['fodp'], + 'application/vnd.oasis.opendocument.presentation-template' => ['otp'], + 'application/vnd.oasis.opendocument.spreadsheet' => ['ods'], + 'application/vnd.oasis.opendocument.spreadsheet-flat-xml' => ['fods'], + 'application/vnd.oasis.opendocument.spreadsheet-template' => ['ots'], + 'application/vnd.oasis.opendocument.text' => ['odt'], + 'application/vnd.oasis.opendocument.text-flat-xml' => ['fodt'], + 'application/vnd.oasis.opendocument.text-master' => ['odm'], + 'application/vnd.oasis.opendocument.text-template' => ['ott'], + 'application/vnd.oasis.opendocument.text-web' => ['oth'], + 'application/vnd.olpc-sugar' => ['xo'], + 'application/vnd.oma.dd2+xml' => ['dd2'], + 'application/vnd.openofficeorg.extension' => ['oxt'], + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => ['pptx'], + 'application/vnd.openxmlformats-officedocument.presentationml.slide' => ['sldx'], + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => ['ppsx'], + 'application/vnd.openxmlformats-officedocument.presentationml.template' => ['potx'], + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => ['xlsx'], + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => ['xltx'], + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => ['docx'], + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => ['dotx'], + 'application/vnd.osgeo.mapguide.package' => ['mgp'], + 'application/vnd.osgi.dp' => ['dp'], + 'application/vnd.osgi.subsystem' => ['esa'], + 'application/vnd.palm' => ['pdb', 'pqa', 'oprc', 'prc'], + 'application/vnd.pawaafile' => ['paw'], + 'application/vnd.pg.format' => ['str'], + 'application/vnd.pg.osasli' => ['ei6'], + 'application/vnd.picsel' => ['efif'], + 'application/vnd.pmi.widget' => ['wg'], + 'application/vnd.pocketlearn' => ['plf'], + 'application/vnd.powerbuilder6' => ['pbd'], + 'application/vnd.previewsystems.box' => ['box'], + 'application/vnd.proteus.magazine' => ['mgz'], + 'application/vnd.publishare-delta-tree' => ['qps'], + 'application/vnd.pvi.ptid1' => ['ptid'], + 'application/vnd.quark.quarkxpress' => ['qxd', 'qxt', 'qwd', 'qwt', 'qxl', 'qxb'], + 'application/vnd.rar' => ['rar'], + 'application/vnd.realvnc.bed' => ['bed'], + 'application/vnd.recordare.musicxml' => ['mxl'], + 'application/vnd.recordare.musicxml+xml' => ['musicxml'], + 'application/vnd.rig.cryptonote' => ['cryptonote'], + 'application/vnd.rim.cod' => ['cod'], + 'application/vnd.rn-realmedia' => ['rm', 'rmj', 'rmm', 'rms', 'rmx', 'rmvb'], + 'application/vnd.rn-realmedia-vbr' => ['rmvb', 'rm', 'rmj', 'rmm', 'rms', 'rmx'], + 'application/vnd.route66.link66+xml' => ['link66'], + 'application/vnd.sailingtracker.track' => ['st'], + 'application/vnd.sdp' => ['sdp'], + 'application/vnd.seemail' => ['see'], + 'application/vnd.sema' => ['sema'], + 'application/vnd.semd' => ['semd'], + 'application/vnd.semf' => ['semf'], + 'application/vnd.shana.informed.formdata' => ['ifm'], + 'application/vnd.shana.informed.formtemplate' => ['itp'], + 'application/vnd.shana.informed.interchange' => ['iif'], + 'application/vnd.shana.informed.package' => ['ipk'], + 'application/vnd.simtech-mindmapper' => ['twd', 'twds'], + 'application/vnd.smaf' => ['mmf', 'smaf'], + 'application/vnd.smart.teacher' => ['teacher'], + 'application/vnd.snap' => ['snap'], + 'application/vnd.solent.sdkm+xml' => ['sdkm', 'sdkd'], + 'application/vnd.spotfire.dxp' => ['dxp'], + 'application/vnd.spotfire.sfs' => ['sfs'], + 'application/vnd.sqlite3' => ['sqlite3'], + 'application/vnd.squashfs' => ['sqsh'], + 'application/vnd.stardivision.calc' => ['sdc'], + 'application/vnd.stardivision.chart' => ['sds'], + 'application/vnd.stardivision.draw' => ['sda'], + 'application/vnd.stardivision.impress' => ['sdd', 'sdp'], + 'application/vnd.stardivision.mail' => ['smd'], + 'application/vnd.stardivision.math' => ['smf'], + 'application/vnd.stardivision.writer' => ['sdw', 'vor', 'sgl'], + 'application/vnd.stardivision.writer-global' => ['sgl', 'sdw', 'vor'], + 'application/vnd.stepmania.package' => ['smzip'], + 'application/vnd.stepmania.stepchart' => ['sm'], + 'application/vnd.sun.xml.base' => ['odb'], + 'application/vnd.sun.xml.calc' => ['sxc'], + 'application/vnd.sun.xml.calc.template' => ['stc'], + 'application/vnd.sun.xml.draw' => ['sxd'], + 'application/vnd.sun.xml.draw.template' => ['std'], + 'application/vnd.sun.xml.impress' => ['sxi'], + 'application/vnd.sun.xml.impress.template' => ['sti'], + 'application/vnd.sun.xml.math' => ['sxm'], + 'application/vnd.sun.xml.writer' => ['sxw'], + 'application/vnd.sun.xml.writer.global' => ['sxg'], + 'application/vnd.sun.xml.writer.template' => ['stw'], + 'application/vnd.sus-calendar' => ['sus', 'susp'], + 'application/vnd.svd' => ['svd'], + 'application/vnd.symbian.install' => ['sis', 'sisx'], + 'application/vnd.syncml+xml' => ['xsm'], + 'application/vnd.syncml.dm+wbxml' => ['bdm'], + 'application/vnd.syncml.dm+xml' => ['xdm'], + 'application/vnd.tao.intent-module-archive' => ['tao'], + 'application/vnd.tcpdump.pcap' => ['pcap', 'cap', 'dmp'], + 'application/vnd.tmobile-livetv' => ['tmo'], + 'application/vnd.trid.tpt' => ['tpt'], + 'application/vnd.triscape.mxs' => ['mxs'], + 'application/vnd.trueapp' => ['tra'], + 'application/vnd.ufdl' => ['ufd', 'ufdl'], + 'application/vnd.uiq.theme' => ['utz'], + 'application/vnd.umajin' => ['umj'], + 'application/vnd.unity' => ['unityweb'], + 'application/vnd.uoml+xml' => ['uoml'], + 'application/vnd.vcx' => ['vcx'], + 'application/vnd.visio' => ['vsd', 'vst', 'vss', 'vsw'], + 'application/vnd.visionary' => ['vis'], + 'application/vnd.vsf' => ['vsf'], + 'application/vnd.wap.wbxml' => ['wbxml'], + 'application/vnd.wap.wmlc' => ['wmlc'], + 'application/vnd.wap.wmlscriptc' => ['wmlsc'], + 'application/vnd.webturbo' => ['wtb'], + 'application/vnd.wolfram.player' => ['nbp'], + 'application/vnd.wordperfect' => ['wpd', 'wp', 'wp4', 'wp5', 'wp6', 'wpp'], + 'application/vnd.wqd' => ['wqd'], + 'application/vnd.wt.stf' => ['stf'], + 'application/vnd.xara' => ['xar'], + 'application/vnd.xdgapp' => ['flatpak', 'xdgapp'], + 'application/vnd.xfdl' => ['xfdl'], + 'application/vnd.yamaha.hv-dic' => ['hvd'], + 'application/vnd.yamaha.hv-script' => ['hvs'], + 'application/vnd.yamaha.hv-voice' => ['hvp'], + 'application/vnd.yamaha.openscoreformat' => ['osf'], + 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => ['osfpvg'], + 'application/vnd.yamaha.smaf-audio' => ['saf'], + 'application/vnd.yamaha.smaf-phrase' => ['spf'], + 'application/vnd.yellowriver-custom-menu' => ['cmp'], + 'application/vnd.youtube.yt' => ['yt'], + 'application/vnd.zul' => ['zir', 'zirz'], + 'application/vnd.zzazz.deck+xml' => ['zaz'], + 'application/voicexml+xml' => ['vxml'], + 'application/widget' => ['wgt'], + 'application/winhlp' => ['hlp'], + 'application/wk1' => ['123', 'wk1', 'wk3', 'wk4', 'wks'], + 'application/wmf' => ['wmf'], + 'application/wordperfect' => ['wp', 'wp4', 'wp5', 'wp6', 'wpd', 'wpp'], + 'application/wsdl+xml' => ['wsdl'], + 'application/wspolicy+xml' => ['wspolicy'], + 'application/wwf' => ['wwf'], + 'application/x-123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'], + 'application/x-7z-compressed' => ['7z'], + 'application/x-abiword' => ['abw', 'abw.CRASHED', 'abw.gz', 'zabw'], + 'application/x-ace' => ['ace'], + 'application/x-ace-compressed' => ['ace'], + 'application/x-alz' => ['alz'], + 'application/x-amiga-disk-format' => ['adf'], + 'application/x-amipro' => ['sam'], + 'application/x-annodex' => ['anx'], + 'application/x-aportisdoc' => ['pdb', 'pdc'], + 'application/x-apple-diskimage' => ['dmg'], + 'application/x-applix-spreadsheet' => ['as'], + 'application/x-applix-word' => ['aw'], + 'application/x-archive' => ['a', 'ar'], + 'application/x-arj' => ['arj'], + 'application/x-asp' => ['asp'], + 'application/x-atari-2600-rom' => ['a26'], + 'application/x-atari-7800-rom' => ['a78'], + 'application/x-atari-lynx-rom' => ['lnx'], + 'application/x-authorware-bin' => ['aab', 'x32', 'u32', 'vox'], + 'application/x-authorware-map' => ['aam'], + 'application/x-authorware-seg' => ['aas'], + 'application/x-awk' => ['awk'], + 'application/x-bcpio' => ['bcpio'], + 'application/x-bittorrent' => ['torrent'], + 'application/x-blender' => ['blender', 'blend', 'BLEND'], + 'application/x-blorb' => ['blb', 'blorb'], + 'application/x-bsdiff' => ['bsdiff'], + 'application/x-bzdvi' => ['dvi.bz2'], + 'application/x-bzip' => ['bz', 'bz2'], + 'application/x-bzip-compressed-tar' => ['tar.bz2', 'tar.bz', 'tbz2', 'tbz', 'tb2'], + 'application/x-bzip2' => ['bz2', 'boz', 'bz'], + 'application/x-bzpdf' => ['pdf.bz2'], + 'application/x-bzpostscript' => ['ps.bz2'], + 'application/x-cb7' => ['cb7'], + 'application/x-cbr' => ['cbr', 'cba', 'cbt', 'cbz', 'cb7'], + 'application/x-cbt' => ['cbt'], + 'application/x-cbz' => ['cbz'], + 'application/x-ccmx' => ['ccmx'], + 'application/x-cd-image' => ['iso', 'iso9660'], + 'application/x-cdlink' => ['vcd'], + 'application/x-cdr' => ['cdr'], + 'application/x-cdrdao-toc' => ['toc'], + 'application/x-cfs-compressed' => ['cfs'], + 'application/x-chat' => ['chat'], + 'application/x-chess-pgn' => ['pgn'], + 'application/x-chm' => ['chm'], + 'application/x-cisco-vpn-settings' => ['pcf'], + 'application/x-compress' => ['Z'], + 'application/x-compressed-tar' => ['tar.gz', 'tgz'], + 'application/x-conference' => ['nsc'], + 'application/x-coreldraw' => ['cdr'], + 'application/x-cpio' => ['cpio'], + 'application/x-cpio-compressed' => ['cpio.gz'], + 'application/x-csh' => ['csh'], + 'application/x-cue' => ['cue'], + 'application/x-dar' => ['dar'], + 'application/x-dbase' => ['dbf'], + 'application/x-dbf' => ['dbf'], + 'application/x-dc-rom' => ['dc'], + 'application/x-deb' => ['deb', 'udeb'], + 'application/x-debian-package' => ['deb', 'udeb'], + 'application/x-designer' => ['ui'], + 'application/x-desktop' => ['desktop', 'kdelnk'], + 'application/x-dgc-compressed' => ['dgc'], + 'application/x-dia-diagram' => ['dia'], + 'application/x-dia-shape' => ['shape'], + 'application/x-director' => ['dir', 'dcr', 'dxr', 'cst', 'cct', 'cxt', 'w3d', 'fgd', 'swa'], + 'application/x-docbook+xml' => ['dbk', 'docbook'], + 'application/x-doom' => ['wad'], + 'application/x-doom-wad' => ['wad'], + 'application/x-dtbncx+xml' => ['ncx'], + 'application/x-dtbook+xml' => ['dtb'], + 'application/x-dtbresource+xml' => ['res'], + 'application/x-dvi' => ['dvi'], + 'application/x-e-theme' => ['etheme'], + 'application/x-egon' => ['egon'], + 'application/x-emf' => ['emf'], + 'application/x-envoy' => ['evy'], + 'application/x-eva' => ['eva'], + 'application/x-fd-file' => ['fd', 'qd'], + 'application/x-fds-disk' => ['fds'], + 'application/x-fictionbook' => ['fb2'], + 'application/x-fictionbook+xml' => ['fb2'], + 'application/x-flash-video' => ['flv'], + 'application/x-fluid' => ['fl'], + 'application/x-font-afm' => ['afm'], + 'application/x-font-bdf' => ['bdf'], + 'application/x-font-ghostscript' => ['gsf'], + 'application/x-font-linux-psf' => ['psf'], + 'application/x-font-otf' => ['otf'], + 'application/x-font-pcf' => ['pcf', 'pcf.Z', 'pcf.gz'], + 'application/x-font-snf' => ['snf'], + 'application/x-font-speedo' => ['spd'], + 'application/x-font-ttf' => ['ttf'], + 'application/x-font-ttx' => ['ttx'], + 'application/x-font-type1' => ['pfa', 'pfb', 'pfm', 'afm', 'gsf'], + 'application/x-font-woff' => ['woff'], + 'application/x-frame' => ['fm'], + 'application/x-freearc' => ['arc'], + 'application/x-futuresplash' => ['spl'], + 'application/x-gameboy-color-rom' => ['gbc', 'cgb'], + 'application/x-gameboy-rom' => ['gb', 'sgb'], + 'application/x-gamecube-iso-image' => ['iso'], + 'application/x-gamecube-rom' => ['iso'], + 'application/x-gamegear-rom' => ['gg'], + 'application/x-gba-rom' => ['gba', 'agb'], + 'application/x-gca-compressed' => ['gca'], + 'application/x-gedcom' => ['ged', 'gedcom'], + 'application/x-genesis-32x-rom' => ['32x', 'mdx'], + 'application/x-genesis-rom' => ['gen', 'smd'], + 'application/x-gettext' => ['po'], + 'application/x-gettext-translation' => ['gmo', 'mo'], + 'application/x-glade' => ['glade'], + 'application/x-glulx' => ['ulx'], + 'application/x-gnome-app-info' => ['desktop', 'kdelnk'], + 'application/x-gnucash' => ['gnucash', 'gnc', 'xac'], + 'application/x-gnumeric' => ['gnumeric'], + 'application/x-gnuplot' => ['gp', 'gplt', 'gnuplot'], + 'application/x-go-sgf' => ['sgf'], + 'application/x-gpx' => ['gpx'], + 'application/x-gpx+xml' => ['gpx'], + 'application/x-gramps-xml' => ['gramps'], + 'application/x-graphite' => ['gra'], + 'application/x-gtar' => ['gtar', 'tar', 'gem'], + 'application/x-gtk-builder' => ['ui'], + 'application/x-gz-font-linux-psf' => ['psf.gz'], + 'application/x-gzdvi' => ['dvi.gz'], + 'application/x-gzip' => ['gz'], + 'application/x-gzpdf' => ['pdf.gz'], + 'application/x-gzpostscript' => ['ps.gz'], + 'application/x-hdf' => ['hdf', 'hdf4', 'h4', 'hdf5', 'h5'], + 'application/x-hfe-file' => ['hfe'], + 'application/x-hfe-floppy-image' => ['hfe'], + 'application/x-hwp' => ['hwp'], + 'application/x-hwt' => ['hwt'], + 'application/x-ica' => ['ica'], + 'application/x-install-instructions' => ['install'], + 'application/x-ipynb+json' => ['ipynb'], + 'application/x-iso9660-appimage' => ['appimage'], + 'application/x-iso9660-image' => ['iso', 'iso9660'], + 'application/x-it87' => ['it87'], + 'application/x-iwork-keynote-sffkey' => ['key'], + 'application/x-jar' => ['jar'], + 'application/x-java' => ['class'], + 'application/x-java-archive' => ['jar'], + 'application/x-java-class' => ['class'], + 'application/x-java-jce-keystore' => ['jceks'], + 'application/x-java-jnlp-file' => ['jnlp'], + 'application/x-java-keystore' => ['jks', 'ks'], + 'application/x-java-pack200' => ['pack'], + 'application/x-java-vm' => ['class'], + 'application/x-javascript' => ['js', 'jsm', 'mjs'], + 'application/x-jbuilder-project' => ['jpr', 'jpx'], + 'application/x-karbon' => ['karbon'], + 'application/x-kchart' => ['chrt'], + 'application/x-kexi-connectiondata' => ['kexic'], + 'application/x-kexiproject-shortcut' => ['kexis'], + 'application/x-kexiproject-sqlite' => ['kexi'], + 'application/x-kexiproject-sqlite2' => ['kexi'], + 'application/x-kexiproject-sqlite3' => ['kexi'], + 'application/x-kformula' => ['kfo'], + 'application/x-killustrator' => ['kil'], + 'application/x-kivio' => ['flw'], + 'application/x-kontour' => ['kon'], + 'application/x-kpovmodeler' => ['kpm'], + 'application/x-kpresenter' => ['kpr', 'kpt'], + 'application/x-krita' => ['kra'], + 'application/x-kspread' => ['ksp'], + 'application/x-kugar' => ['kud'], + 'application/x-kword' => ['kwd', 'kwt'], + 'application/x-latex' => ['latex'], + 'application/x-lha' => ['lha', 'lzh'], + 'application/x-lhz' => ['lhz'], + 'application/x-linguist' => ['ts'], + 'application/x-lotus123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'], + 'application/x-lrzip' => ['lrz'], + 'application/x-lrzip-compressed-tar' => ['tar.lrz', 'tlrz'], + 'application/x-lyx' => ['lyx'], + 'application/x-lz4' => ['lz4'], + 'application/x-lz4-compressed-tar' => ['tar.lz4'], + 'application/x-lzh-compressed' => ['lzh', 'lha'], + 'application/x-lzip' => ['lz'], + 'application/x-lzip-compressed-tar' => ['tar.lz'], + 'application/x-lzma' => ['lzma'], + 'application/x-lzma-compressed-tar' => ['tar.lzma', 'tlz'], + 'application/x-lzop' => ['lzo'], + 'application/x-lzpdf' => ['pdf.lz'], + 'application/x-m4' => ['m4'], + 'application/x-magicpoint' => ['mgp'], + 'application/x-markaby' => ['mab'], + 'application/x-mathematica' => ['nb'], + 'application/x-mdb' => ['mdb'], + 'application/x-mie' => ['mie'], + 'application/x-mif' => ['mif'], + 'application/x-mimearchive' => ['mhtml', 'mht'], + 'application/x-mobipocket-ebook' => ['prc', 'mobi'], + 'application/x-ms-application' => ['application'], + 'application/x-ms-asx' => ['asx', 'wax', 'wvx', 'wmx'], + 'application/x-ms-dos-executable' => ['exe'], + 'application/x-ms-shortcut' => ['lnk'], + 'application/x-ms-wim' => ['wim', 'swm'], + 'application/x-ms-wmd' => ['wmd'], + 'application/x-ms-wmz' => ['wmz'], + 'application/x-ms-xbap' => ['xbap'], + 'application/x-msaccess' => ['mdb'], + 'application/x-msbinder' => ['obd'], + 'application/x-mscardfile' => ['crd'], + 'application/x-msclip' => ['clp'], + 'application/x-msdownload' => ['exe', 'dll', 'com', 'bat', 'msi'], + 'application/x-msexcel' => ['xls', 'xlc', 'xll', 'xlm', 'xlw', 'xla', 'xlt', 'xld'], + 'application/x-msi' => ['msi'], + 'application/x-msmediaview' => ['mvb', 'm13', 'm14'], + 'application/x-msmetafile' => ['wmf', 'wmz', 'emf', 'emz'], + 'application/x-msmoney' => ['mny'], + 'application/x-mspowerpoint' => ['ppz', 'ppt', 'pps', 'pot'], + 'application/x-mspublisher' => ['pub'], + 'application/x-msschedule' => ['scd'], + 'application/x-msterminal' => ['trm'], + 'application/x-mswinurl' => ['url'], + 'application/x-msword' => ['doc'], + 'application/x-mswrite' => ['wri'], + 'application/x-msx-rom' => ['msx'], + 'application/x-n64-rom' => ['n64', 'z64', 'v64'], + 'application/x-navi-animation' => ['ani'], + 'application/x-neo-geo-pocket-color-rom' => ['ngc'], + 'application/x-neo-geo-pocket-rom' => ['ngp'], + 'application/x-nes-rom' => ['nes', 'nez', 'unf', 'unif'], + 'application/x-netcdf' => ['nc', 'cdf'], + 'application/x-netshow-channel' => ['nsc'], + 'application/x-nintendo-ds-rom' => ['nds'], + 'application/x-nzb' => ['nzb'], + 'application/x-object' => ['o'], + 'application/x-ogg' => ['ogx'], + 'application/x-oleo' => ['oleo'], + 'application/x-pagemaker' => ['p65', 'pm', 'pm6', 'pmd'], + 'application/x-pak' => ['pak'], + 'application/x-palm-database' => ['prc', 'pdb', 'pqa', 'oprc'], + 'application/x-par2' => ['PAR2', 'par2'], + 'application/x-partial-download' => ['wkdownload', 'crdownload', 'part'], + 'application/x-pc-engine-rom' => ['pce'], + 'application/x-pcap' => ['pcap', 'cap', 'dmp'], + 'application/x-pdf' => ['pdf'], + 'application/x-perl' => ['pl', 'PL', 'pm', 'al', 'perl', 'pod', 't'], + 'application/x-photoshop' => ['psd'], + 'application/x-php' => ['php', 'php3', 'php4', 'php5', 'phps'], + 'application/x-pkcs12' => ['p12', 'pfx'], + 'application/x-pkcs7-certificates' => ['p7b', 'spc'], + 'application/x-pkcs7-certreqresp' => ['p7r'], + 'application/x-planperfect' => ['pln'], + 'application/x-pocket-word' => ['psw'], + 'application/x-pw' => ['pw'], + 'application/x-python-bytecode' => ['pyc', 'pyo'], + 'application/x-qpress' => ['qp'], + 'application/x-qtiplot' => ['qti', 'qti.gz'], + 'application/x-quattropro' => ['wb1', 'wb2', 'wb3'], + 'application/x-quicktime-media-link' => ['qtl'], + 'application/x-quicktimeplayer' => ['qtl'], + 'application/x-qw' => ['qif'], + 'application/x-rar' => ['rar'], + 'application/x-rar-compressed' => ['rar'], + 'application/x-raw-disk-image' => ['raw-disk-image', 'img'], + 'application/x-raw-disk-image-xz-compressed' => ['raw-disk-image.xz', 'img.xz'], + 'application/x-raw-floppy-disk-image' => ['fd', 'qd'], + 'application/x-redhat-package-manager' => ['rpm'], + 'application/x-reject' => ['rej'], + 'application/x-research-info-systems' => ['ris'], + 'application/x-rnc' => ['rnc'], + 'application/x-rpm' => ['rpm'], + 'application/x-ruby' => ['rb'], + 'application/x-sami' => ['smi', 'sami'], + 'application/x-sap-file' => ['sap'], + 'application/x-saturn-rom' => ['bin', 'iso'], + 'application/x-sdp' => ['sdp'], + 'application/x-sega-cd-rom' => ['bin', 'iso'], + 'application/x-sg1000-rom' => ['sg'], + 'application/x-sh' => ['sh'], + 'application/x-shar' => ['shar'], + 'application/x-shared-library-la' => ['la'], + 'application/x-sharedlib' => ['so'], + 'application/x-shellscript' => ['sh'], + 'application/x-shockwave-flash' => ['swf', 'spl'], + 'application/x-shorten' => ['shn'], + 'application/x-siag' => ['siag'], + 'application/x-silverlight-app' => ['xap'], + 'application/x-sit' => ['sit'], + 'application/x-smaf' => ['mmf', 'smaf'], + 'application/x-sms-rom' => ['sms'], + 'application/x-snes-rom' => ['sfc', 'smc'], + 'application/x-source-rpm' => ['src.rpm', 'spm'], + 'application/x-spss-por' => ['por'], + 'application/x-spss-sav' => ['sav', 'zsav'], + 'application/x-spss-savefile' => ['sav', 'zsav'], + 'application/x-sql' => ['sql'], + 'application/x-sqlite2' => ['sqlite2'], + 'application/x-sqlite3' => ['sqlite3'], + 'application/x-srt' => ['srt'], + 'application/x-stuffit' => ['sit'], + 'application/x-stuffitx' => ['sitx'], + 'application/x-subrip' => ['srt'], + 'application/x-sv4cpio' => ['sv4cpio'], + 'application/x-sv4crc' => ['sv4crc'], + 'application/x-t3vm-image' => ['t3'], + 'application/x-t602' => ['602'], + 'application/x-tads' => ['gam'], + 'application/x-tar' => ['tar', 'gtar', 'gem'], + 'application/x-tarz' => ['tar.Z', 'taz'], + 'application/x-tcl' => ['tcl'], + 'application/x-tex' => ['tex', 'ltx', 'sty', 'cls', 'dtx', 'ins', 'latex'], + 'application/x-tex-gf' => ['gf'], + 'application/x-tex-pk' => ['pk'], + 'application/x-tex-tfm' => ['tfm'], + 'application/x-texinfo' => ['texinfo', 'texi'], + 'application/x-tgif' => ['obj'], + 'application/x-theme' => ['theme'], + 'application/x-thomson-cartridge-memo7' => ['m7'], + 'application/x-thomson-cassette' => ['k7'], + 'application/x-thomson-sap-image' => ['sap'], + 'application/x-trash' => ['bak', 'old', 'sik'], + 'application/x-trig' => ['trig'], + 'application/x-troff' => ['tr', 'roff', 't'], + 'application/x-troff-man' => ['man'], + 'application/x-tzo' => ['tar.lzo', 'tzo'], + 'application/x-ufraw' => ['ufraw'], + 'application/x-ustar' => ['ustar'], + 'application/x-virtual-boy-rom' => ['vb'], + 'application/x-vnd.kde.kexi' => ['kexi'], + 'application/x-wais-source' => ['src'], + 'application/x-wbfs' => ['iso'], + 'application/x-wia' => ['iso'], + 'application/x-wii-iso-image' => ['iso'], + 'application/x-wii-rom' => ['iso'], + 'application/x-wii-wad' => ['wad'], + 'application/x-windows-themepack' => ['themepack'], + 'application/x-wmf' => ['wmf'], + 'application/x-wonderswan-color-rom' => ['wsc'], + 'application/x-wonderswan-rom' => ['ws'], + 'application/x-wordperfect' => ['wp', 'wp4', 'wp5', 'wp6', 'wpd', 'wpp'], + 'application/x-wpg' => ['wpg'], + 'application/x-wwf' => ['wwf'], + 'application/x-x509-ca-cert' => ['der', 'crt', 'cert', 'pem'], + 'application/x-xar' => ['xar', 'pkg'], + 'application/x-xbel' => ['xbel'], + 'application/x-xfig' => ['fig'], + 'application/x-xliff' => ['xlf', 'xliff'], + 'application/x-xliff+xml' => ['xlf'], + 'application/x-xpinstall' => ['xpi'], + 'application/x-xspf+xml' => ['xspf'], + 'application/x-xz' => ['xz'], + 'application/x-xz-compressed-tar' => ['tar.xz', 'txz'], + 'application/x-xzpdf' => ['pdf.xz'], + 'application/x-yaml' => ['yaml', 'yml'], + 'application/x-zip' => ['zip'], + 'application/x-zip-compressed' => ['zip'], + 'application/x-zip-compressed-fb2' => ['fb2.zip'], + 'application/x-zmachine' => ['z1', 'z2', 'z3', 'z4', 'z5', 'z6', 'z7', 'z8'], + 'application/x-zoo' => ['zoo'], + 'application/xaml+xml' => ['xaml'], + 'application/xcap-diff+xml' => ['xdf'], + 'application/xenc+xml' => ['xenc'], + 'application/xhtml+xml' => ['xhtml', 'xht'], + 'application/xliff+xml' => ['xlf', 'xliff'], + 'application/xml' => ['xml', 'xsl', 'xbl', 'xsd', 'rng'], + 'application/xml-dtd' => ['dtd'], + 'application/xml-external-parsed-entity' => ['ent'], + 'application/xop+xml' => ['xop'], + 'application/xproc+xml' => ['xpl'], + 'application/xps' => ['oxps', 'xps'], + 'application/xslt+xml' => ['xslt', 'xsl'], + 'application/xspf+xml' => ['xspf'], + 'application/xv+xml' => ['mxml', 'xhvml', 'xvml', 'xvm'], + 'application/yang' => ['yang'], + 'application/yin+xml' => ['yin'], + 'application/zip' => ['zip'], + 'application/zlib' => ['zz'], + 'audio/3gpp' => ['3gp', '3gpp', '3ga'], + 'audio/3gpp-encrypted' => ['3gp', '3gpp', '3ga'], + 'audio/3gpp2' => ['3g2', '3gp2', '3gpp2'], + 'audio/aac' => ['aac', 'adts', 'ass'], + 'audio/ac3' => ['ac3'], + 'audio/adpcm' => ['adp'], + 'audio/amr' => ['amr'], + 'audio/amr-encrypted' => ['amr'], + 'audio/amr-wb' => ['awb'], + 'audio/amr-wb-encrypted' => ['awb'], + 'audio/annodex' => ['axa'], + 'audio/basic' => ['au', 'snd'], + 'audio/flac' => ['flac'], + 'audio/imelody' => ['imy', 'ime'], + 'audio/m3u' => ['m3u', 'm3u8', 'vlc'], + 'audio/m4a' => ['m4a', 'f4a'], + 'audio/midi' => ['mid', 'midi', 'kar', 'rmi'], + 'audio/mobile-xmf' => ['xmf'], + 'audio/mp2' => ['mp2'], + 'audio/mp3' => ['mp3', 'mpga'], + 'audio/mp4' => ['m4a', 'mp4a', 'f4a'], + 'audio/mpeg' => ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'], + 'audio/mpegurl' => ['m3u', 'm3u8', 'vlc'], + 'audio/ogg' => ['oga', 'ogg', 'spx', 'opus'], + 'audio/prs.sid' => ['sid', 'psid'], + 'audio/s3m' => ['s3m'], + 'audio/scpls' => ['pls'], + 'audio/silk' => ['sil'], + 'audio/tta' => ['tta'], + 'audio/usac' => ['loas', 'xhe'], + 'audio/vnd.audible' => ['aa', 'aax'], + 'audio/vnd.audible.aax' => ['aa', 'aax'], + 'audio/vnd.dece.audio' => ['uva', 'uvva'], + 'audio/vnd.digital-winds' => ['eol'], + 'audio/vnd.dra' => ['dra'], + 'audio/vnd.dts' => ['dts'], + 'audio/vnd.dts.hd' => ['dtshd'], + 'audio/vnd.lucent.voice' => ['lvp'], + 'audio/vnd.m-realaudio' => ['ra', 'rax'], + 'audio/vnd.ms-playready.media.pya' => ['pya'], + 'audio/vnd.nuera.ecelp4800' => ['ecelp4800'], + 'audio/vnd.nuera.ecelp7470' => ['ecelp7470'], + 'audio/vnd.nuera.ecelp9600' => ['ecelp9600'], + 'audio/vnd.rip' => ['rip'], + 'audio/vnd.rn-realaudio' => ['ra', 'rax'], + 'audio/vnd.wave' => ['wav'], + 'audio/vorbis' => ['oga', 'ogg'], + 'audio/wav' => ['wav'], + 'audio/webm' => ['weba'], + 'audio/wma' => ['wma'], + 'audio/x-aac' => ['aac', 'adts', 'ass'], + 'audio/x-aifc' => ['aifc', 'aiffc'], + 'audio/x-aiff' => ['aif', 'aiff', 'aifc'], + 'audio/x-aiffc' => ['aifc', 'aiffc'], + 'audio/x-amzxml' => ['amz'], + 'audio/x-annodex' => ['axa'], + 'audio/x-ape' => ['ape'], + 'audio/x-caf' => ['caf'], + 'audio/x-dts' => ['dts'], + 'audio/x-dtshd' => ['dtshd'], + 'audio/x-flac' => ['flac'], + 'audio/x-flac+ogg' => ['oga', 'ogg'], + 'audio/x-gsm' => ['gsm'], + 'audio/x-hx-aac-adts' => ['aac', 'adts', 'ass'], + 'audio/x-imelody' => ['imy', 'ime'], + 'audio/x-iriver-pla' => ['pla'], + 'audio/x-it' => ['it'], + 'audio/x-m3u' => ['m3u', 'm3u8', 'vlc'], + 'audio/x-m4a' => ['m4a', 'f4a'], + 'audio/x-m4b' => ['m4b', 'f4b'], + 'audio/x-m4r' => ['m4r'], + 'audio/x-matroska' => ['mka'], + 'audio/x-midi' => ['mid', 'midi', 'kar'], + 'audio/x-minipsf' => ['minipsf'], + 'audio/x-mo3' => ['mo3'], + 'audio/x-mod' => ['mod', 'ult', 'uni', 'm15', 'mtm', '669', 'med'], + 'audio/x-mp2' => ['mp2'], + 'audio/x-mp3' => ['mp3', 'mpga'], + 'audio/x-mp3-playlist' => ['m3u', 'm3u8', 'vlc'], + 'audio/x-mpeg' => ['mp3', 'mpga'], + 'audio/x-mpegurl' => ['m3u', 'm3u8', 'vlc'], + 'audio/x-mpg' => ['mp3', 'mpga'], + 'audio/x-ms-asx' => ['asx', 'wax', 'wvx', 'wmx'], + 'audio/x-ms-wax' => ['wax'], + 'audio/x-ms-wma' => ['wma'], + 'audio/x-musepack' => ['mpc', 'mpp', 'mp+'], + 'audio/x-ogg' => ['oga', 'ogg', 'opus'], + 'audio/x-oggflac' => ['oga', 'ogg'], + 'audio/x-opus+ogg' => ['opus'], + 'audio/x-pn-audibleaudio' => ['aa', 'aax'], + 'audio/x-pn-realaudio' => ['ram', 'ra', 'rax'], + 'audio/x-pn-realaudio-plugin' => ['rmp'], + 'audio/x-psf' => ['psf'], + 'audio/x-psflib' => ['psflib'], + 'audio/x-rn-3gpp-amr' => ['3gp', '3gpp', '3ga'], + 'audio/x-rn-3gpp-amr-encrypted' => ['3gp', '3gpp', '3ga'], + 'audio/x-rn-3gpp-amr-wb' => ['3gp', '3gpp', '3ga'], + 'audio/x-rn-3gpp-amr-wb-encrypted' => ['3gp', '3gpp', '3ga'], + 'audio/x-s3m' => ['s3m'], + 'audio/x-scpls' => ['pls'], + 'audio/x-shorten' => ['shn'], + 'audio/x-speex' => ['spx'], + 'audio/x-speex+ogg' => ['oga', 'ogg'], + 'audio/x-stm' => ['stm'], + 'audio/x-tta' => ['tta'], + 'audio/x-voc' => ['voc'], + 'audio/x-vorbis' => ['oga', 'ogg'], + 'audio/x-vorbis+ogg' => ['oga', 'ogg'], + 'audio/x-wav' => ['wav'], + 'audio/x-wavpack' => ['wv', 'wvp'], + 'audio/x-wavpack-correction' => ['wvc'], + 'audio/x-xi' => ['xi'], + 'audio/x-xm' => ['xm'], + 'audio/x-xmf' => ['xmf'], + 'audio/xm' => ['xm'], + 'audio/xmf' => ['xmf'], + 'chemical/x-cdx' => ['cdx'], + 'chemical/x-cif' => ['cif'], + 'chemical/x-cmdf' => ['cmdf'], + 'chemical/x-cml' => ['cml'], + 'chemical/x-csml' => ['csml'], + 'chemical/x-xyz' => ['xyz'], + 'flv-application/octet-stream' => ['flv'], + 'font/collection' => ['ttc'], + 'font/otf' => ['otf'], + 'font/ttf' => ['ttf'], + 'font/woff' => ['woff', 'woff2'], + 'font/woff2' => ['woff2'], + 'image/bmp' => ['bmp', 'dib'], + 'image/cdr' => ['cdr'], + 'image/cgm' => ['cgm'], + 'image/emf' => ['emf'], + 'image/fax-g3' => ['g3'], + 'image/fits' => ['fits'], + 'image/g3fax' => ['g3'], + 'image/gif' => ['gif'], + 'image/heic' => ['heic', 'heif'], + 'image/heic-sequence' => ['heic', 'heif'], + 'image/heif' => ['heic', 'heif'], + 'image/heif-sequence' => ['heic', 'heif'], + 'image/ico' => ['ico'], + 'image/icon' => ['ico'], + 'image/ief' => ['ief'], + 'image/jp2' => ['jp2', 'jpg2'], + 'image/jpeg' => ['jpeg', 'jpg', 'jpe'], + 'image/jpeg2000' => ['jp2', 'jpg2'], + 'image/jpeg2000-image' => ['jp2', 'jpg2'], + 'image/jpm' => ['jpm', 'jpgm'], + 'image/jpx' => ['jpf', 'jpx'], + 'image/ktx' => ['ktx'], + 'image/openraster' => ['ora'], + 'image/pdf' => ['pdf'], + 'image/photoshop' => ['psd'], + 'image/pjpeg' => ['jpeg', 'jpg', 'jpe'], + 'image/png' => ['png'], + 'image/prs.btif' => ['btif'], + 'image/psd' => ['psd'], + 'image/rle' => ['rle'], + 'image/sgi' => ['sgi'], + 'image/svg' => ['svg'], + 'image/svg+xml' => ['svg', 'svgz'], + 'image/svg+xml-compressed' => ['svgz'], + 'image/tiff' => ['tiff', 'tif'], + 'image/vnd.adobe.photoshop' => ['psd'], + 'image/vnd.dece.graphic' => ['uvi', 'uvvi', 'uvg', 'uvvg'], + 'image/vnd.djvu' => ['djvu', 'djv'], + 'image/vnd.djvu+multipage' => ['djvu', 'djv'], + 'image/vnd.dvb.subtitle' => ['sub'], + 'image/vnd.dwg' => ['dwg'], + 'image/vnd.dxf' => ['dxf'], + 'image/vnd.fastbidsheet' => ['fbs'], + 'image/vnd.fpx' => ['fpx'], + 'image/vnd.fst' => ['fst'], + 'image/vnd.fujixerox.edmics-mmr' => ['mmr'], + 'image/vnd.fujixerox.edmics-rlc' => ['rlc'], + 'image/vnd.microsoft.icon' => ['ico'], + 'image/vnd.ms-modi' => ['mdi'], + 'image/vnd.ms-photo' => ['wdp'], + 'image/vnd.net-fpx' => ['npx'], + 'image/vnd.rn-realpix' => ['rp'], + 'image/vnd.wap.wbmp' => ['wbmp'], + 'image/vnd.xiff' => ['xif'], + 'image/vnd.zbrush.pcx' => ['pcx'], + 'image/webp' => ['webp'], + 'image/wmf' => ['wmf'], + 'image/x-3ds' => ['3ds'], + 'image/x-adobe-dng' => ['dng'], + 'image/x-applix-graphics' => ['ag'], + 'image/x-bmp' => ['bmp', 'dib'], + 'image/x-bzeps' => ['eps.bz2', 'epsi.bz2', 'epsf.bz2'], + 'image/x-canon-cr2' => ['cr2'], + 'image/x-canon-crw' => ['crw'], + 'image/x-cdr' => ['cdr'], + 'image/x-cmu-raster' => ['ras'], + 'image/x-cmx' => ['cmx'], + 'image/x-compressed-xcf' => ['xcf.gz', 'xcf.bz2'], + 'image/x-dds' => ['dds'], + 'image/x-djvu' => ['djvu', 'djv'], + 'image/x-emf' => ['emf'], + 'image/x-eps' => ['eps', 'epsi', 'epsf'], + 'image/x-exr' => ['exr'], + 'image/x-fits' => ['fits'], + 'image/x-freehand' => ['fh', 'fhc', 'fh4', 'fh5', 'fh7'], + 'image/x-fuji-raf' => ['raf'], + 'image/x-gimp-gbr' => ['gbr'], + 'image/x-gimp-gih' => ['gih'], + 'image/x-gimp-pat' => ['pat'], + 'image/x-gzeps' => ['eps.gz', 'epsi.gz', 'epsf.gz'], + 'image/x-icb' => ['tga', 'icb', 'tpic', 'vda', 'vst'], + 'image/x-icns' => ['icns'], + 'image/x-ico' => ['ico'], + 'image/x-icon' => ['ico'], + 'image/x-iff' => ['iff', 'ilbm', 'lbm'], + 'image/x-ilbm' => ['iff', 'ilbm', 'lbm'], + 'image/x-jng' => ['jng'], + 'image/x-jp2-codestream' => ['j2c', 'j2k', 'jpc'], + 'image/x-jpeg2000-image' => ['jp2', 'jpg2'], + 'image/x-kodak-dcr' => ['dcr'], + 'image/x-kodak-k25' => ['k25'], + 'image/x-kodak-kdc' => ['kdc'], + 'image/x-lwo' => ['lwo', 'lwob'], + 'image/x-lws' => ['lws'], + 'image/x-macpaint' => ['pntg'], + 'image/x-minolta-mrw' => ['mrw'], + 'image/x-mrsid-image' => ['sid'], + 'image/x-ms-bmp' => ['bmp', 'dib'], + 'image/x-msod' => ['msod'], + 'image/x-nikon-nef' => ['nef'], + 'image/x-olympus-orf' => ['orf'], + 'image/x-panasonic-raw' => ['raw'], + 'image/x-panasonic-raw2' => ['rw2'], + 'image/x-panasonic-rw' => ['raw'], + 'image/x-panasonic-rw2' => ['rw2'], + 'image/x-pcx' => ['pcx'], + 'image/x-pentax-pef' => ['pef'], + 'image/x-photo-cd' => ['pcd'], + 'image/x-photoshop' => ['psd'], + 'image/x-pict' => ['pic', 'pct', 'pict', 'pict1', 'pict2'], + 'image/x-portable-anymap' => ['pnm'], + 'image/x-portable-bitmap' => ['pbm'], + 'image/x-portable-graymap' => ['pgm'], + 'image/x-portable-pixmap' => ['ppm'], + 'image/x-psd' => ['psd'], + 'image/x-quicktime' => ['qtif', 'qif'], + 'image/x-rgb' => ['rgb'], + 'image/x-sgi' => ['sgi'], + 'image/x-sigma-x3f' => ['x3f'], + 'image/x-skencil' => ['sk', 'sk1'], + 'image/x-sony-arw' => ['arw'], + 'image/x-sony-sr2' => ['sr2'], + 'image/x-sony-srf' => ['srf'], + 'image/x-sun-raster' => ['sun'], + 'image/x-tga' => ['tga', 'icb', 'tpic', 'vda', 'vst'], + 'image/x-win-bitmap' => ['cur'], + 'image/x-win-metafile' => ['wmf'], + 'image/x-wmf' => ['wmf'], + 'image/x-xbitmap' => ['xbm'], + 'image/x-xcf' => ['xcf'], + 'image/x-xfig' => ['fig'], + 'image/x-xpixmap' => ['xpm'], + 'image/x-xpm' => ['xpm'], + 'image/x-xwindowdump' => ['xwd'], + 'image/x.djvu' => ['djvu', 'djv'], + 'message/rfc822' => ['eml', 'mime'], + 'model/iges' => ['igs', 'iges'], + 'model/mesh' => ['msh', 'mesh', 'silo'], + 'model/stl' => ['stl'], + 'model/vnd.collada+xml' => ['dae'], + 'model/vnd.dwf' => ['dwf'], + 'model/vnd.gdl' => ['gdl'], + 'model/vnd.gtw' => ['gtw'], + 'model/vnd.mts' => ['mts'], + 'model/vnd.vtu' => ['vtu'], + 'model/vrml' => ['wrl', 'vrml', 'vrm'], + 'model/x.stl-ascii' => ['stl'], + 'model/x.stl-binary' => ['stl'], + 'model/x3d+binary' => ['x3db', 'x3dbz'], + 'model/x3d+vrml' => ['x3dv', 'x3dvz'], + 'model/x3d+xml' => ['x3d', 'x3dz'], + 'text/cache-manifest' => ['appcache', 'manifest'], + 'text/calendar' => ['ics', 'ifb', 'vcs'], + 'text/css' => ['css'], + 'text/csv' => ['csv'], + 'text/csv-schema' => ['csvs'], + 'text/directory' => ['vcard', 'vcf', 'vct', 'gcrd'], + 'text/ecmascript' => ['es'], + 'text/gedcom' => ['ged', 'gedcom'], + 'text/google-video-pointer' => ['gvp'], + 'text/html' => ['html', 'htm'], + 'text/ico' => ['ico'], + 'text/javascript' => ['js', 'jsm', 'mjs'], + 'text/markdown' => ['md', 'mkd', 'markdown'], + 'text/mathml' => ['mml'], + 'text/n3' => ['n3'], + 'text/plain' => ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'asc'], + 'text/prs.lines.tag' => ['dsc'], + 'text/rdf' => ['rdf', 'rdfs', 'owl'], + 'text/richtext' => ['rtx'], + 'text/rss' => ['rss'], + 'text/rtf' => ['rtf'], + 'text/rust' => ['rs'], + 'text/sgml' => ['sgml', 'sgm'], + 'text/spreadsheet' => ['sylk', 'slk'], + 'text/tab-separated-values' => ['tsv'], + 'text/troff' => ['t', 'tr', 'roff', 'man', 'me', 'ms'], + 'text/turtle' => ['ttl'], + 'text/uri-list' => ['uri', 'uris', 'urls'], + 'text/vcard' => ['vcard', 'vcf', 'vct', 'gcrd'], + 'text/vnd.curl' => ['curl'], + 'text/vnd.curl.dcurl' => ['dcurl'], + 'text/vnd.curl.mcurl' => ['mcurl'], + 'text/vnd.curl.scurl' => ['scurl'], + 'text/vnd.dvb.subtitle' => ['sub'], + 'text/vnd.fly' => ['fly'], + 'text/vnd.fmi.flexstor' => ['flx'], + 'text/vnd.graphviz' => ['gv', 'dot'], + 'text/vnd.in3d.3dml' => ['3dml'], + 'text/vnd.in3d.spot' => ['spot'], + 'text/vnd.qt.linguist' => ['ts'], + 'text/vnd.rn-realtext' => ['rt'], + 'text/vnd.sun.j2me.app-descriptor' => ['jad'], + 'text/vnd.trolltech.linguist' => ['ts'], + 'text/vnd.wap.wml' => ['wml'], + 'text/vnd.wap.wmlscript' => ['wmls'], + 'text/vtt' => ['vtt'], + 'text/x-adasrc' => ['adb', 'ads'], + 'text/x-asm' => ['s', 'asm'], + 'text/x-bibtex' => ['bib'], + 'text/x-c' => ['c', 'cc', 'cxx', 'cpp', 'h', 'hh', 'dic'], + 'text/x-c++hdr' => ['hh', 'hp', 'hpp', 'h++', 'hxx'], + 'text/x-c++src' => ['cpp', 'cxx', 'cc', 'C', 'c++'], + 'text/x-chdr' => ['h'], + 'text/x-cmake' => ['cmake'], + 'text/x-cobol' => ['cbl', 'cob'], + 'text/x-comma-separated-values' => ['csv'], + 'text/x-csharp' => ['cs'], + 'text/x-csrc' => ['c'], + 'text/x-csv' => ['csv'], + 'text/x-dbus-service' => ['service'], + 'text/x-dcl' => ['dcl'], + 'text/x-diff' => ['diff', 'patch'], + 'text/x-dsl' => ['dsl'], + 'text/x-dsrc' => ['d', 'di'], + 'text/x-dtd' => ['dtd'], + 'text/x-eiffel' => ['e', 'eif'], + 'text/x-emacs-lisp' => ['el'], + 'text/x-erlang' => ['erl'], + 'text/x-fortran' => ['f', 'for', 'f77', 'f90', 'f95'], + 'text/x-genie' => ['gs'], + 'text/x-gettext-translation' => ['po'], + 'text/x-gettext-translation-template' => ['pot'], + 'text/x-gherkin' => ['feature'], + 'text/x-go' => ['go'], + 'text/x-google-video-pointer' => ['gvp'], + 'text/x-haskell' => ['hs'], + 'text/x-idl' => ['idl'], + 'text/x-imelody' => ['imy', 'ime'], + 'text/x-iptables' => ['iptables'], + 'text/x-java' => ['java'], + 'text/x-java-source' => ['java'], + 'text/x-ldif' => ['ldif'], + 'text/x-lilypond' => ['ly'], + 'text/x-literate-haskell' => ['lhs'], + 'text/x-log' => ['log'], + 'text/x-lua' => ['lua'], + 'text/x-lyx' => ['lyx'], + 'text/x-makefile' => ['mk', 'mak'], + 'text/x-markdown' => ['md', 'mkd', 'markdown'], + 'text/x-matlab' => ['m'], + 'text/x-microdvd' => ['sub'], + 'text/x-moc' => ['moc'], + 'text/x-modelica' => ['mo'], + 'text/x-mof' => ['mof'], + 'text/x-mpsub' => ['sub'], + 'text/x-mrml' => ['mrml', 'mrl'], + 'text/x-ms-regedit' => ['reg'], + 'text/x-mup' => ['mup', 'not'], + 'text/x-nfo' => ['nfo'], + 'text/x-objcsrc' => ['m'], + 'text/x-ocaml' => ['ml', 'mli'], + 'text/x-ocl' => ['ocl'], + 'text/x-octave' => ['m'], + 'text/x-ooc' => ['ooc'], + 'text/x-opencl-src' => ['cl'], + 'text/x-opml' => ['opml'], + 'text/x-opml+xml' => ['opml'], + 'text/x-pascal' => ['p', 'pas'], + 'text/x-patch' => ['diff', 'patch'], + 'text/x-perl' => ['pl', 'PL', 'pm', 'al', 'perl', 'pod', 't'], + 'text/x-po' => ['po'], + 'text/x-pot' => ['pot'], + 'text/x-python' => ['py', 'pyx', 'wsgi'], + 'text/x-python3' => ['py', 'py3', 'py3x'], + 'text/x-qml' => ['qml', 'qmltypes', 'qmlproject'], + 'text/x-reject' => ['rej'], + 'text/x-rpm-spec' => ['spec'], + 'text/x-sass' => ['sass'], + 'text/x-scala' => ['scala'], + 'text/x-scheme' => ['scm', 'ss'], + 'text/x-scss' => ['scss'], + 'text/x-setext' => ['etx'], + 'text/x-sfv' => ['sfv'], + 'text/x-sh' => ['sh'], + 'text/x-sql' => ['sql'], + 'text/x-ssa' => ['ssa', 'ass'], + 'text/x-subviewer' => ['sub'], + 'text/x-svhdr' => ['svh'], + 'text/x-svsrc' => ['sv'], + 'text/x-systemd-unit' => ['automount', 'device', 'mount', 'path', 'scope', 'service', 'slice', 'socket', 'swap', 'target', 'timer'], + 'text/x-tcl' => ['tcl', 'tk'], + 'text/x-tex' => ['tex', 'ltx', 'sty', 'cls', 'dtx', 'ins', 'latex'], + 'text/x-texinfo' => ['texi', 'texinfo'], + 'text/x-troff' => ['tr', 'roff', 't'], + 'text/x-troff-me' => ['me'], + 'text/x-troff-mm' => ['mm'], + 'text/x-troff-ms' => ['ms'], + 'text/x-twig' => ['twig'], + 'text/x-txt2tags' => ['t2t'], + 'text/x-uil' => ['uil'], + 'text/x-uuencode' => ['uu', 'uue'], + 'text/x-vala' => ['vala', 'vapi'], + 'text/x-vcalendar' => ['vcs', 'ics'], + 'text/x-vcard' => ['vcf', 'vcard', 'vct', 'gcrd'], + 'text/x-verilog' => ['v'], + 'text/x-vhdl' => ['vhd', 'vhdl'], + 'text/x-xmi' => ['xmi'], + 'text/x-xslfo' => ['fo', 'xslfo'], + 'text/x-yaml' => ['yaml', 'yml'], + 'text/x.gcode' => ['gcode'], + 'text/xml' => ['xml', 'xbl', 'xsd', 'rng'], + 'text/xml-external-parsed-entity' => ['ent'], + 'text/yaml' => ['yaml', 'yml'], + 'video/3gp' => ['3gp', '3gpp', '3ga'], + 'video/3gpp' => ['3gp', '3gpp', '3ga'], + 'video/3gpp-encrypted' => ['3gp', '3gpp', '3ga'], + 'video/3gpp2' => ['3g2', '3gp2', '3gpp2'], + 'video/annodex' => ['axv'], + 'video/avi' => ['avi', 'avf', 'divx'], + 'video/divx' => ['avi', 'avf', 'divx'], + 'video/dv' => ['dv'], + 'video/fli' => ['fli', 'flc'], + 'video/flv' => ['flv'], + 'video/h261' => ['h261'], + 'video/h263' => ['h263'], + 'video/h264' => ['h264'], + 'video/jpeg' => ['jpgv'], + 'video/jpm' => ['jpm', 'jpgm'], + 'video/mj2' => ['mj2', 'mjp2'], + 'video/mp2t' => ['m2t', 'm2ts', 'ts', 'mts', 'cpi', 'clpi', 'mpl', 'mpls', 'bdm', 'bdmv'], + 'video/mp4' => ['mp4', 'mp4v', 'mpg4', 'm4v', 'f4v', 'lrv'], + 'video/mp4v-es' => ['mp4', 'm4v', 'f4v', 'lrv'], + 'video/mpeg' => ['mpeg', 'mpg', 'mpe', 'm1v', 'm2v', 'mp2', 'vob'], + 'video/mpeg-system' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'], + 'video/msvideo' => ['avi', 'avf', 'divx'], + 'video/ogg' => ['ogv', 'ogg'], + 'video/quicktime' => ['qt', 'mov', 'moov', 'qtvr'], + 'video/vivo' => ['viv', 'vivo'], + 'video/vnd.dece.hd' => ['uvh', 'uvvh'], + 'video/vnd.dece.mobile' => ['uvm', 'uvvm'], + 'video/vnd.dece.pd' => ['uvp', 'uvvp'], + 'video/vnd.dece.sd' => ['uvs', 'uvvs'], + 'video/vnd.dece.video' => ['uvv', 'uvvv'], + 'video/vnd.divx' => ['avi', 'avf', 'divx'], + 'video/vnd.dvb.file' => ['dvb'], + 'video/vnd.fvt' => ['fvt'], + 'video/vnd.mpegurl' => ['mxu', 'm4u', 'm1u'], + 'video/vnd.ms-playready.media.pyv' => ['pyv'], + 'video/vnd.rn-realvideo' => ['rv', 'rvx'], + 'video/vnd.uvvu.mp4' => ['uvu', 'uvvu'], + 'video/vnd.vivo' => ['viv', 'vivo'], + 'video/webm' => ['webm'], + 'video/x-anim' => ['anim[1-9j]'], + 'video/x-annodex' => ['axv'], + 'video/x-avi' => ['avi', 'avf', 'divx'], + 'video/x-f4v' => ['f4v'], + 'video/x-fli' => ['fli', 'flc'], + 'video/x-flic' => ['fli', 'flc'], + 'video/x-flv' => ['flv'], + 'video/x-javafx' => ['fxm'], + 'video/x-m4v' => ['m4v', 'mp4', 'f4v', 'lrv'], + 'video/x-matroska' => ['mkv', 'mk3d', 'mks'], + 'video/x-matroska-3d' => ['mk3d'], + 'video/x-mjpeg' => ['mjpeg', 'mjpg'], + 'video/x-mng' => ['mng'], + 'video/x-mpeg' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'], + 'video/x-mpeg-system' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'], + 'video/x-mpeg2' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'], + 'video/x-mpegurl' => ['m1u', 'm4u', 'mxu'], + 'video/x-ms-asf' => ['asf', 'asx'], + 'video/x-ms-asf-plugin' => ['asf'], + 'video/x-ms-vob' => ['vob'], + 'video/x-ms-wax' => ['asx', 'wax', 'wvx', 'wmx'], + 'video/x-ms-wm' => ['wm', 'asf'], + 'video/x-ms-wmv' => ['wmv'], + 'video/x-ms-wmx' => ['wmx', 'asx', 'wax', 'wvx'], + 'video/x-ms-wvx' => ['wvx', 'asx', 'wax', 'wmx'], + 'video/x-msvideo' => ['avi', 'avf', 'divx'], + 'video/x-nsv' => ['nsv'], + 'video/x-ogg' => ['ogv', 'ogg'], + 'video/x-ogm' => ['ogm'], + 'video/x-ogm+ogg' => ['ogm'], + 'video/x-real-video' => ['rv', 'rvx'], + 'video/x-sgi-movie' => ['movie'], + 'video/x-smv' => ['smv'], + 'video/x-theora' => ['ogg'], + 'video/x-theora+ogg' => ['ogg'], + 'x-conference/x-cooltalk' => ['ice'], + 'x-epoc/x-sisx-app' => ['sisx'], + 'zz-application/zz-winassoc-123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'], + 'zz-application/zz-winassoc-cab' => ['cab'], + 'zz-application/zz-winassoc-cdr' => ['cdr'], + 'zz-application/zz-winassoc-doc' => ['doc'], + 'zz-application/zz-winassoc-hlp' => ['hlp'], + 'zz-application/zz-winassoc-mdb' => ['mdb'], + 'zz-application/zz-winassoc-uu' => ['uue'], + 'zz-application/zz-winassoc-xls' => ['xls', 'xlc', 'xll', 'xlm', 'xlw', 'xla', 'xlt', 'xld'], + ]; + + private static $reverseMap = [ + '32x' => ['application/x-genesis-32x-rom'], + '3dml' => ['text/vnd.in3d.3dml'], + '3ds' => ['image/x-3ds'], + '3g2' => ['audio/3gpp2', 'video/3gpp2'], + '3ga' => ['audio/3gpp', 'audio/3gpp-encrypted', 'audio/x-rn-3gpp-amr', 'audio/x-rn-3gpp-amr-encrypted', 'audio/x-rn-3gpp-amr-wb', 'audio/x-rn-3gpp-amr-wb-encrypted', 'video/3gp', 'video/3gpp', 'video/3gpp-encrypted'], + '3gp' => ['audio/3gpp', 'audio/3gpp-encrypted', 'audio/x-rn-3gpp-amr', 'audio/x-rn-3gpp-amr-encrypted', 'audio/x-rn-3gpp-amr-wb', 'audio/x-rn-3gpp-amr-wb-encrypted', 'video/3gp', 'video/3gpp', 'video/3gpp-encrypted'], + '3gp2' => ['audio/3gpp2', 'video/3gpp2'], + '3gpp' => ['audio/3gpp', 'audio/3gpp-encrypted', 'audio/x-rn-3gpp-amr', 'audio/x-rn-3gpp-amr-encrypted', 'audio/x-rn-3gpp-amr-wb', 'audio/x-rn-3gpp-amr-wb-encrypted', 'video/3gp', 'video/3gpp', 'video/3gpp-encrypted'], + '3gpp2' => ['audio/3gpp2', 'video/3gpp2'], + '7z' => ['application/x-7z-compressed'], + 'BLEND' => ['application/x-blender'], + 'C' => ['text/x-c++src'], + 'PAR2' => ['application/x-par2'], + 'PL' => ['application/x-perl', 'text/x-perl'], + 'Z' => ['application/x-compress'], + 'a' => ['application/x-archive'], + 'a26' => ['application/x-atari-2600-rom'], + 'a78' => ['application/x-atari-7800-rom'], + 'aa' => ['audio/vnd.audible', 'audio/vnd.audible.aax', 'audio/x-pn-audibleaudio'], + 'aab' => ['application/x-authorware-bin'], + 'aac' => ['audio/aac', 'audio/x-aac', 'audio/x-hx-aac-adts'], + 'aam' => ['application/x-authorware-map'], + 'aas' => ['application/x-authorware-seg'], + 'aax' => ['audio/vnd.audible', 'audio/vnd.audible.aax', 'audio/x-pn-audibleaudio'], + 'abw' => ['application/x-abiword'], + 'abw.CRASHED' => ['application/x-abiword'], + 'abw.gz' => ['application/x-abiword'], + 'ac' => ['application/pkix-attr-cert'], + 'ac3' => ['audio/ac3'], + 'acc' => ['application/vnd.americandynamics.acc'], + 'ace' => ['application/x-ace', 'application/x-ace-compressed'], + 'acu' => ['application/vnd.acucobol'], + 'acutc' => ['application/vnd.acucorp'], + 'adb' => ['text/x-adasrc'], + 'adf' => ['application/x-amiga-disk-format'], + 'adp' => ['audio/adpcm'], + 'ads' => ['text/x-adasrc'], + 'adts' => ['audio/aac', 'audio/x-aac', 'audio/x-hx-aac-adts'], + 'aep' => ['application/vnd.audiograph'], + 'afm' => ['application/x-font-afm', 'application/x-font-type1'], + 'afp' => ['application/vnd.ibm.modcap'], + 'ag' => ['image/x-applix-graphics'], + 'agb' => ['application/x-gba-rom'], + 'ahead' => ['application/vnd.ahead.space'], + 'ai' => ['application/illustrator', 'application/postscript', 'application/vnd.adobe.illustrator'], + 'aif' => ['audio/x-aiff'], + 'aifc' => ['audio/x-aifc', 'audio/x-aiff', 'audio/x-aiffc'], + 'aiff' => ['audio/x-aiff'], + 'aiffc' => ['audio/x-aifc', 'audio/x-aiffc'], + 'air' => ['application/vnd.adobe.air-application-installer-package+zip'], + 'ait' => ['application/vnd.dvb.ait'], + 'al' => ['application/x-perl', 'text/x-perl'], + 'alz' => ['application/x-alz'], + 'ami' => ['application/vnd.amiga.ami'], + 'amr' => ['audio/amr', 'audio/amr-encrypted'], + 'amz' => ['audio/x-amzxml'], + 'ani' => ['application/x-navi-animation'], + 'anim[1-9j]' => ['video/x-anim'], + 'anx' => ['application/annodex', 'application/x-annodex'], + 'ape' => ['audio/x-ape'], + 'apk' => ['application/vnd.android.package-archive'], + 'appcache' => ['text/cache-manifest'], + 'appimage' => ['application/vnd.appimage', 'application/x-iso9660-appimage'], + 'application' => ['application/x-ms-application'], + 'apr' => ['application/vnd.lotus-approach'], + 'aps' => ['application/postscript'], + 'ar' => ['application/x-archive'], + 'arc' => ['application/x-freearc'], + 'arj' => ['application/x-arj'], + 'arw' => ['image/x-sony-arw'], + 'as' => ['application/x-applix-spreadsheet'], + 'asc' => ['application/pgp', 'application/pgp-encrypted', 'application/pgp-keys', 'application/pgp-signature', 'text/plain'], + 'asf' => ['application/vnd.ms-asf', 'video/x-ms-asf', 'video/x-ms-asf-plugin', 'video/x-ms-wm'], + 'asm' => ['text/x-asm'], + 'aso' => ['application/vnd.accpac.simply.aso'], + 'asp' => ['application/x-asp'], + 'ass' => ['audio/aac', 'audio/x-aac', 'audio/x-hx-aac-adts', 'text/x-ssa'], + 'asx' => ['application/x-ms-asx', 'audio/x-ms-asx', 'video/x-ms-asf', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'], + 'atc' => ['application/vnd.acucorp'], + 'atom' => ['application/atom+xml'], + 'atomcat' => ['application/atomcat+xml'], + 'atomsvc' => ['application/atomsvc+xml'], + 'atx' => ['application/vnd.antix.game-component'], + 'au' => ['audio/basic'], + 'automount' => ['text/x-systemd-unit'], + 'avf' => ['video/avi', 'video/divx', 'video/msvideo', 'video/vnd.divx', 'video/x-avi', 'video/x-msvideo'], + 'avi' => ['video/avi', 'video/divx', 'video/msvideo', 'video/vnd.divx', 'video/x-avi', 'video/x-msvideo'], + 'aw' => ['application/applixware', 'application/x-applix-word'], + 'awb' => ['audio/amr-wb', 'audio/amr-wb-encrypted'], + 'awk' => ['application/x-awk'], + 'axa' => ['audio/annodex', 'audio/x-annodex'], + 'axv' => ['video/annodex', 'video/x-annodex'], + 'azf' => ['application/vnd.airzip.filesecure.azf'], + 'azs' => ['application/vnd.airzip.filesecure.azs'], + 'azw' => ['application/vnd.amazon.ebook'], + 'bak' => ['application/x-trash'], + 'bat' => ['application/x-msdownload'], + 'bcpio' => ['application/x-bcpio'], + 'bdf' => ['application/x-font-bdf'], + 'bdm' => ['application/vnd.syncml.dm+wbxml', 'video/mp2t'], + 'bdmv' => ['video/mp2t'], + 'bed' => ['application/vnd.realvnc.bed'], + 'bh2' => ['application/vnd.fujitsu.oasysprs'], + 'bib' => ['text/x-bibtex'], + 'bin' => ['application/octet-stream', 'application/x-saturn-rom', 'application/x-sega-cd-rom'], + 'blb' => ['application/x-blorb'], + 'blend' => ['application/x-blender'], + 'blender' => ['application/x-blender'], + 'blorb' => ['application/x-blorb'], + 'bmi' => ['application/vnd.bmi'], + 'bmp' => ['image/bmp', 'image/x-bmp', 'image/x-ms-bmp'], + 'book' => ['application/vnd.framemaker'], + 'box' => ['application/vnd.previewsystems.box'], + 'boz' => ['application/x-bzip2'], + 'bpk' => ['application/octet-stream'], + 'bsdiff' => ['application/x-bsdiff'], + 'btif' => ['image/prs.btif'], + 'bz' => ['application/x-bzip', 'application/x-bzip2'], + 'bz2' => ['application/x-bz2', 'application/x-bzip', 'application/x-bzip2'], + 'c' => ['text/x-c', 'text/x-csrc'], + 'c++' => ['text/x-c++src'], + 'c11amc' => ['application/vnd.cluetrust.cartomobile-config'], + 'c11amz' => ['application/vnd.cluetrust.cartomobile-config-pkg'], + 'c4d' => ['application/vnd.clonk.c4group'], + 'c4f' => ['application/vnd.clonk.c4group'], + 'c4g' => ['application/vnd.clonk.c4group'], + 'c4p' => ['application/vnd.clonk.c4group'], + 'c4u' => ['application/vnd.clonk.c4group'], + 'cab' => ['application/vnd.ms-cab-compressed', 'zz-application/zz-winassoc-cab'], + 'caf' => ['audio/x-caf'], + 'cap' => ['application/pcap', 'application/vnd.tcpdump.pcap', 'application/x-pcap'], + 'car' => ['application/vnd.curl.car'], + 'cat' => ['application/vnd.ms-pki.seccat'], + 'cb7' => ['application/x-cb7', 'application/x-cbr'], + 'cba' => ['application/x-cbr'], + 'cbl' => ['text/x-cobol'], + 'cbr' => ['application/vnd.comicbook-rar', 'application/x-cbr'], + 'cbt' => ['application/x-cbr', 'application/x-cbt'], + 'cbz' => ['application/vnd.comicbook+zip', 'application/x-cbr', 'application/x-cbz'], + 'cc' => ['text/x-c', 'text/x-c++src'], + 'ccmx' => ['application/x-ccmx'], + 'cct' => ['application/x-director'], + 'ccxml' => ['application/ccxml+xml'], + 'cdbcmsg' => ['application/vnd.contact.cmsg'], + 'cdf' => ['application/x-netcdf'], + 'cdkey' => ['application/vnd.mediastation.cdkey'], + 'cdmia' => ['application/cdmi-capability'], + 'cdmic' => ['application/cdmi-container'], + 'cdmid' => ['application/cdmi-domain'], + 'cdmio' => ['application/cdmi-object'], + 'cdmiq' => ['application/cdmi-queue'], + 'cdr' => ['application/cdr', 'application/coreldraw', 'application/vnd.corel-draw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'], + 'cdx' => ['chemical/x-cdx'], + 'cdxml' => ['application/vnd.chemdraw+xml'], + 'cdy' => ['application/vnd.cinderella'], + 'cer' => ['application/pkix-cert'], + 'cert' => ['application/x-x509-ca-cert'], + 'cfs' => ['application/x-cfs-compressed'], + 'cgb' => ['application/x-gameboy-color-rom'], + 'cgm' => ['image/cgm'], + 'chat' => ['application/x-chat'], + 'chm' => ['application/vnd.ms-htmlhelp', 'application/x-chm'], + 'chrt' => ['application/vnd.kde.kchart', 'application/x-kchart'], + 'cif' => ['chemical/x-cif'], + 'cii' => ['application/vnd.anser-web-certificate-issue-initiation'], + 'cil' => ['application/vnd.ms-artgalry'], + 'cl' => ['text/x-opencl-src'], + 'cla' => ['application/vnd.claymore'], + 'class' => ['application/java', 'application/java-byte-code', 'application/java-vm', 'application/x-java', 'application/x-java-class', 'application/x-java-vm'], + 'clkk' => ['application/vnd.crick.clicker.keyboard'], + 'clkp' => ['application/vnd.crick.clicker.palette'], + 'clkt' => ['application/vnd.crick.clicker.template'], + 'clkw' => ['application/vnd.crick.clicker.wordbank'], + 'clkx' => ['application/vnd.crick.clicker'], + 'clp' => ['application/x-msclip'], + 'clpi' => ['video/mp2t'], + 'cls' => ['application/x-tex', 'text/x-tex'], + 'cmake' => ['text/x-cmake'], + 'cmc' => ['application/vnd.cosmocaller'], + 'cmdf' => ['chemical/x-cmdf'], + 'cml' => ['chemical/x-cml'], + 'cmp' => ['application/vnd.yellowriver-custom-menu'], + 'cmx' => ['image/x-cmx'], + 'cob' => ['text/x-cobol'], + 'cod' => ['application/vnd.rim.cod'], + 'coffee' => ['application/vnd.coffeescript'], + 'com' => ['application/x-msdownload'], + 'conf' => ['text/plain'], + 'cpi' => ['video/mp2t'], + 'cpio' => ['application/x-cpio'], + 'cpio.gz' => ['application/x-cpio-compressed'], + 'cpp' => ['text/x-c', 'text/x-c++src'], + 'cpt' => ['application/mac-compactpro'], + 'cr2' => ['image/x-canon-cr2'], + 'crd' => ['application/x-mscardfile'], + 'crdownload' => ['application/x-partial-download'], + 'crl' => ['application/pkix-crl'], + 'crt' => ['application/x-x509-ca-cert'], + 'crw' => ['image/x-canon-crw'], + 'cryptonote' => ['application/vnd.rig.cryptonote'], + 'cs' => ['text/x-csharp'], + 'csh' => ['application/x-csh'], + 'csml' => ['chemical/x-csml'], + 'csp' => ['application/vnd.commonspace'], + 'css' => ['text/css'], + 'cst' => ['application/x-director'], + 'csv' => ['text/csv', 'text/x-comma-separated-values', 'text/x-csv'], + 'csvs' => ['text/csv-schema'], + 'cu' => ['application/cu-seeme'], + 'cue' => ['application/x-cue'], + 'cur' => ['image/x-win-bitmap'], + 'curl' => ['text/vnd.curl'], + 'cww' => ['application/prs.cww'], + 'cxt' => ['application/x-director'], + 'cxx' => ['text/x-c', 'text/x-c++src'], + 'd' => ['text/x-dsrc'], + 'dae' => ['model/vnd.collada+xml'], + 'daf' => ['application/vnd.mobius.daf'], + 'dar' => ['application/x-dar'], + 'dart' => ['application/vnd.dart'], + 'dataless' => ['application/vnd.fdsn.seed'], + 'davmount' => ['application/davmount+xml'], + 'dbf' => ['application/dbase', 'application/dbf', 'application/x-dbase', 'application/x-dbf'], + 'dbk' => ['application/docbook+xml', 'application/vnd.oasis.docbook+xml', 'application/x-docbook+xml'], + 'dc' => ['application/x-dc-rom'], + 'dcl' => ['text/x-dcl'], + 'dcm' => ['application/dicom'], + 'dcr' => ['application/x-director', 'image/x-kodak-dcr'], + 'dcurl' => ['text/vnd.curl.dcurl'], + 'dd2' => ['application/vnd.oma.dd2+xml'], + 'ddd' => ['application/vnd.fujixerox.ddd'], + 'dds' => ['image/x-dds'], + 'deb' => ['application/vnd.debian.binary-package', 'application/x-deb', 'application/x-debian-package'], + 'def' => ['text/plain'], + 'deploy' => ['application/octet-stream'], + 'der' => ['application/x-x509-ca-cert'], + 'desktop' => ['application/x-desktop', 'application/x-gnome-app-info'], + 'device' => ['text/x-systemd-unit'], + 'dfac' => ['application/vnd.dreamfactory'], + 'dgc' => ['application/x-dgc-compressed'], + 'di' => ['text/x-dsrc'], + 'dia' => ['application/x-dia-diagram'], + 'dib' => ['image/bmp', 'image/x-bmp', 'image/x-ms-bmp'], + 'dic' => ['text/x-c'], + 'diff' => ['text/x-diff', 'text/x-patch'], + 'dir' => ['application/x-director'], + 'dis' => ['application/vnd.mobius.dis'], + 'dist' => ['application/octet-stream'], + 'distz' => ['application/octet-stream'], + 'divx' => ['video/avi', 'video/divx', 'video/msvideo', 'video/vnd.divx', 'video/x-avi', 'video/x-msvideo'], + 'djv' => ['image/vnd.djvu', 'image/vnd.djvu+multipage', 'image/x-djvu', 'image/x.djvu'], + 'djvu' => ['image/vnd.djvu', 'image/vnd.djvu+multipage', 'image/x-djvu', 'image/x.djvu'], + 'dll' => ['application/x-msdownload'], + 'dmg' => ['application/x-apple-diskimage'], + 'dmp' => ['application/pcap', 'application/vnd.tcpdump.pcap', 'application/x-pcap'], + 'dms' => ['application/octet-stream'], + 'dna' => ['application/vnd.dna'], + 'dng' => ['image/x-adobe-dng'], + 'doc' => ['application/msword', 'application/vnd.ms-word', 'application/x-msword', 'zz-application/zz-winassoc-doc'], + 'docbook' => ['application/docbook+xml', 'application/vnd.oasis.docbook+xml', 'application/x-docbook+xml'], + 'docm' => ['application/vnd.ms-word.document.macroenabled.12'], + 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], + 'dot' => ['application/msword', 'application/msword-template', 'text/vnd.graphviz'], + 'dotm' => ['application/vnd.ms-word.template.macroenabled.12'], + 'dotx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.template'], + 'dp' => ['application/vnd.osgi.dp'], + 'dpg' => ['application/vnd.dpgraph'], + 'dra' => ['audio/vnd.dra'], + 'dsc' => ['text/prs.lines.tag'], + 'dsl' => ['text/x-dsl'], + 'dssc' => ['application/dssc+der'], + 'dtb' => ['application/x-dtbook+xml'], + 'dtd' => ['application/xml-dtd', 'text/x-dtd'], + 'dts' => ['audio/vnd.dts', 'audio/x-dts'], + 'dtshd' => ['audio/vnd.dts.hd', 'audio/x-dtshd'], + 'dtx' => ['application/x-tex', 'text/x-tex'], + 'dump' => ['application/octet-stream'], + 'dv' => ['video/dv'], + 'dvb' => ['video/vnd.dvb.file'], + 'dvi' => ['application/x-dvi'], + 'dvi.bz2' => ['application/x-bzdvi'], + 'dvi.gz' => ['application/x-gzdvi'], + 'dwf' => ['model/vnd.dwf'], + 'dwg' => ['image/vnd.dwg'], + 'dxf' => ['image/vnd.dxf'], + 'dxp' => ['application/vnd.spotfire.dxp'], + 'dxr' => ['application/x-director'], + 'e' => ['text/x-eiffel'], + 'ecelp4800' => ['audio/vnd.nuera.ecelp4800'], + 'ecelp7470' => ['audio/vnd.nuera.ecelp7470'], + 'ecelp9600' => ['audio/vnd.nuera.ecelp9600'], + 'ecma' => ['application/ecmascript'], + 'edm' => ['application/vnd.novadigm.edm'], + 'edx' => ['application/vnd.novadigm.edx'], + 'efif' => ['application/vnd.picsel'], + 'egon' => ['application/x-egon'], + 'ei6' => ['application/vnd.pg.osasli'], + 'eif' => ['text/x-eiffel'], + 'el' => ['text/x-emacs-lisp'], + 'elc' => ['application/octet-stream'], + 'emf' => ['application/emf', 'application/x-emf', 'application/x-msmetafile', 'image/emf', 'image/x-emf'], + 'eml' => ['message/rfc822'], + 'emma' => ['application/emma+xml'], + 'emp' => ['application/vnd.emusic-emusic_package'], + 'emz' => ['application/x-msmetafile'], + 'ent' => ['application/xml-external-parsed-entity', 'text/xml-external-parsed-entity'], + 'eol' => ['audio/vnd.digital-winds'], + 'eot' => ['application/vnd.ms-fontobject'], + 'eps' => ['application/postscript', 'image/x-eps'], + 'eps.bz2' => ['image/x-bzeps'], + 'eps.gz' => ['image/x-gzeps'], + 'epsf' => ['image/x-eps'], + 'epsf.bz2' => ['image/x-bzeps'], + 'epsf.gz' => ['image/x-gzeps'], + 'epsi' => ['image/x-eps'], + 'epsi.bz2' => ['image/x-bzeps'], + 'epsi.gz' => ['image/x-gzeps'], + 'epub' => ['application/epub+zip'], + 'erl' => ['text/x-erlang'], + 'es' => ['application/ecmascript', 'text/ecmascript'], + 'es3' => ['application/vnd.eszigno3+xml'], + 'esa' => ['application/vnd.osgi.subsystem'], + 'esf' => ['application/vnd.epson.esf'], + 'et3' => ['application/vnd.eszigno3+xml'], + 'etheme' => ['application/x-e-theme'], + 'etx' => ['text/x-setext'], + 'eva' => ['application/x-eva'], + 'evy' => ['application/x-envoy'], + 'exe' => ['application/x-ms-dos-executable', 'application/x-msdownload'], + 'exi' => ['application/exi'], + 'exr' => ['image/x-exr'], + 'ext' => ['application/vnd.novadigm.ext'], + 'ez' => ['application/andrew-inset'], + 'ez2' => ['application/vnd.ezpix-album'], + 'ez3' => ['application/vnd.ezpix-package'], + 'f' => ['text/x-fortran'], + 'f4a' => ['audio/m4a', 'audio/mp4', 'audio/x-m4a'], + 'f4b' => ['audio/x-m4b'], + 'f4v' => ['video/mp4', 'video/mp4v-es', 'video/x-f4v', 'video/x-m4v'], + 'f77' => ['text/x-fortran'], + 'f90' => ['text/x-fortran'], + 'f95' => ['text/x-fortran'], + 'fb2' => ['application/x-fictionbook', 'application/x-fictionbook+xml'], + 'fb2.zip' => ['application/x-zip-compressed-fb2'], + 'fbs' => ['image/vnd.fastbidsheet'], + 'fcdt' => ['application/vnd.adobe.formscentral.fcdt'], + 'fcs' => ['application/vnd.isac.fcs'], + 'fd' => ['application/x-fd-file', 'application/x-raw-floppy-disk-image'], + 'fdf' => ['application/vnd.fdf'], + 'fds' => ['application/x-fds-disk'], + 'fe_launch' => ['application/vnd.denovo.fcselayout-link'], + 'feature' => ['text/x-gherkin'], + 'fg5' => ['application/vnd.fujitsu.oasysgp'], + 'fgd' => ['application/x-director'], + 'fh' => ['image/x-freehand'], + 'fh4' => ['image/x-freehand'], + 'fh5' => ['image/x-freehand'], + 'fh7' => ['image/x-freehand'], + 'fhc' => ['image/x-freehand'], + 'fig' => ['application/x-xfig', 'image/x-xfig'], + 'fits' => ['image/fits', 'image/x-fits'], + 'fl' => ['application/x-fluid'], + 'flac' => ['audio/flac', 'audio/x-flac'], + 'flatpak' => ['application/vnd.flatpak', 'application/vnd.xdgapp'], + 'flatpakref' => ['application/vnd.flatpak.ref'], + 'flatpakrepo' => ['application/vnd.flatpak.repo'], + 'flc' => ['video/fli', 'video/x-fli', 'video/x-flic'], + 'fli' => ['video/fli', 'video/x-fli', 'video/x-flic'], + 'flo' => ['application/vnd.micrografx.flo'], + 'flv' => ['video/x-flv', 'application/x-flash-video', 'flv-application/octet-stream', 'video/flv'], + 'flw' => ['application/vnd.kde.kivio', 'application/x-kivio'], + 'flx' => ['text/vnd.fmi.flexstor'], + 'fly' => ['text/vnd.fly'], + 'fm' => ['application/vnd.framemaker', 'application/x-frame'], + 'fnc' => ['application/vnd.frogans.fnc'], + 'fo' => ['text/x-xslfo'], + 'fodg' => ['application/vnd.oasis.opendocument.graphics-flat-xml'], + 'fodp' => ['application/vnd.oasis.opendocument.presentation-flat-xml'], + 'fods' => ['application/vnd.oasis.opendocument.spreadsheet-flat-xml'], + 'fodt' => ['application/vnd.oasis.opendocument.text-flat-xml'], + 'for' => ['text/x-fortran'], + 'fpx' => ['image/vnd.fpx'], + 'frame' => ['application/vnd.framemaker'], + 'fsc' => ['application/vnd.fsc.weblaunch'], + 'fst' => ['image/vnd.fst'], + 'ftc' => ['application/vnd.fluxtime.clip'], + 'fti' => ['application/vnd.anser-web-funds-transfer-initiation'], + 'fvt' => ['video/vnd.fvt'], + 'fxm' => ['video/x-javafx'], + 'fxp' => ['application/vnd.adobe.fxp'], + 'fxpl' => ['application/vnd.adobe.fxp'], + 'fzs' => ['application/vnd.fuzzysheet'], + 'g2w' => ['application/vnd.geoplan'], + 'g3' => ['image/fax-g3', 'image/g3fax'], + 'g3w' => ['application/vnd.geospace'], + 'gac' => ['application/vnd.groove-account'], + 'gam' => ['application/x-tads'], + 'gb' => ['application/x-gameboy-rom'], + 'gba' => ['application/x-gba-rom'], + 'gbc' => ['application/x-gameboy-color-rom'], + 'gbr' => ['application/rpki-ghostbusters', 'image/x-gimp-gbr'], + 'gca' => ['application/x-gca-compressed'], + 'gcode' => ['text/x.gcode'], + 'gcrd' => ['text/directory', 'text/vcard', 'text/x-vcard'], + 'gdl' => ['model/vnd.gdl'], + 'ged' => ['application/x-gedcom', 'text/gedcom'], + 'gedcom' => ['application/x-gedcom', 'text/gedcom'], + 'gem' => ['application/x-gtar', 'application/x-tar'], + 'gen' => ['application/x-genesis-rom'], + 'geo' => ['application/vnd.dynageo'], + 'geo.json' => ['application/geo+json', 'application/vnd.geo+json'], + 'geojson' => ['application/geo+json', 'application/vnd.geo+json'], + 'gex' => ['application/vnd.geometry-explorer'], + 'gf' => ['application/x-tex-gf'], + 'gg' => ['application/x-gamegear-rom'], + 'ggb' => ['application/vnd.geogebra.file'], + 'ggt' => ['application/vnd.geogebra.tool'], + 'ghf' => ['application/vnd.groove-help'], + 'gif' => ['image/gif'], + 'gih' => ['image/x-gimp-gih'], + 'gim' => ['application/vnd.groove-identity-message'], + 'glade' => ['application/x-glade'], + 'gml' => ['application/gml+xml'], + 'gmo' => ['application/x-gettext-translation'], + 'gmx' => ['application/vnd.gmx'], + 'gnc' => ['application/x-gnucash'], + 'gnd' => ['application/gnunet-directory'], + 'gnucash' => ['application/x-gnucash'], + 'gnumeric' => ['application/x-gnumeric'], + 'gnuplot' => ['application/x-gnuplot'], + 'go' => ['text/x-go'], + 'gp' => ['application/x-gnuplot'], + 'gpg' => ['application/pgp', 'application/pgp-encrypted', 'application/pgp-keys', 'application/pgp-signature'], + 'gph' => ['application/vnd.flographit'], + 'gplt' => ['application/x-gnuplot'], + 'gpx' => ['application/gpx', 'application/gpx+xml', 'application/x-gpx', 'application/x-gpx+xml'], + 'gqf' => ['application/vnd.grafeq'], + 'gqs' => ['application/vnd.grafeq'], + 'gra' => ['application/x-graphite'], + 'gram' => ['application/srgs'], + 'gramps' => ['application/x-gramps-xml'], + 'gre' => ['application/vnd.geometry-explorer'], + 'grv' => ['application/vnd.groove-injector'], + 'grxml' => ['application/srgs+xml'], + 'gs' => ['text/x-genie'], + 'gsf' => ['application/x-font-ghostscript', 'application/x-font-type1'], + 'gsm' => ['audio/x-gsm'], + 'gtar' => ['application/x-gtar', 'application/x-tar'], + 'gtm' => ['application/vnd.groove-tool-message'], + 'gtw' => ['model/vnd.gtw'], + 'gv' => ['text/vnd.graphviz'], + 'gvp' => ['text/google-video-pointer', 'text/x-google-video-pointer'], + 'gxf' => ['application/gxf'], + 'gxt' => ['application/vnd.geonext'], + 'gz' => ['application/x-gzip', 'application/gzip'], + 'h' => ['text/x-c', 'text/x-chdr'], + 'h++' => ['text/x-c++hdr'], + 'h261' => ['video/h261'], + 'h263' => ['video/h263'], + 'h264' => ['video/h264'], + 'h4' => ['application/x-hdf'], + 'h5' => ['application/x-hdf'], + 'hal' => ['application/vnd.hal+xml'], + 'hbci' => ['application/vnd.hbci'], + 'hdf' => ['application/x-hdf'], + 'hdf4' => ['application/x-hdf'], + 'hdf5' => ['application/x-hdf'], + 'heic' => ['image/heic', 'image/heic-sequence', 'image/heif', 'image/heif-sequence'], + 'heif' => ['image/heic', 'image/heic-sequence', 'image/heif', 'image/heif-sequence'], + 'hfe' => ['application/x-hfe-file', 'application/x-hfe-floppy-image'], + 'hh' => ['text/x-c', 'text/x-c++hdr'], + 'hlp' => ['application/winhlp', 'zz-application/zz-winassoc-hlp'], + 'hp' => ['text/x-c++hdr'], + 'hpgl' => ['application/vnd.hp-hpgl'], + 'hpid' => ['application/vnd.hp-hpid'], + 'hpp' => ['text/x-c++hdr'], + 'hps' => ['application/vnd.hp-hps'], + 'hqx' => ['application/stuffit', 'application/mac-binhex40'], + 'hs' => ['text/x-haskell'], + 'htke' => ['application/vnd.kenameaapp'], + 'htm' => ['text/html'], + 'html' => ['text/html'], + 'hvd' => ['application/vnd.yamaha.hv-dic'], + 'hvp' => ['application/vnd.yamaha.hv-voice'], + 'hvs' => ['application/vnd.yamaha.hv-script'], + 'hwp' => ['application/vnd.haansoft-hwp', 'application/x-hwp'], + 'hwt' => ['application/vnd.haansoft-hwt', 'application/x-hwt'], + 'hxx' => ['text/x-c++hdr'], + 'i2g' => ['application/vnd.intergeo'], + 'ica' => ['application/x-ica'], + 'icb' => ['image/x-icb', 'image/x-tga'], + 'icc' => ['application/vnd.iccprofile'], + 'ice' => ['x-conference/x-cooltalk'], + 'icm' => ['application/vnd.iccprofile'], + 'icns' => ['image/x-icns'], + 'ico' => ['application/ico', 'image/ico', 'image/icon', 'image/vnd.microsoft.icon', 'image/x-ico', 'image/x-icon', 'text/ico'], + 'ics' => ['application/ics', 'text/calendar', 'text/x-vcalendar'], + 'idl' => ['text/x-idl'], + 'ief' => ['image/ief'], + 'ifb' => ['text/calendar'], + 'iff' => ['image/x-iff', 'image/x-ilbm'], + 'ifm' => ['application/vnd.shana.informed.formdata'], + 'iges' => ['model/iges'], + 'igl' => ['application/vnd.igloader'], + 'igm' => ['application/vnd.insors.igm'], + 'igs' => ['model/iges'], + 'igx' => ['application/vnd.micrografx.igx'], + 'iif' => ['application/vnd.shana.informed.interchange'], + 'ilbm' => ['image/x-iff', 'image/x-ilbm'], + 'ime' => ['audio/imelody', 'audio/x-imelody', 'text/x-imelody'], + 'img' => ['application/x-raw-disk-image'], + 'img.xz' => ['application/x-raw-disk-image-xz-compressed'], + 'imp' => ['application/vnd.accpac.simply.imp'], + 'ims' => ['application/vnd.ms-ims'], + 'imy' => ['audio/imelody', 'audio/x-imelody', 'text/x-imelody'], + 'in' => ['text/plain'], + 'ink' => ['application/inkml+xml'], + 'inkml' => ['application/inkml+xml'], + 'ins' => ['application/x-tex', 'text/x-tex'], + 'install' => ['application/x-install-instructions'], + 'iota' => ['application/vnd.astraea-software.iota'], + 'ipfix' => ['application/ipfix'], + 'ipk' => ['application/vnd.shana.informed.package'], + 'iptables' => ['text/x-iptables'], + 'ipynb' => ['application/x-ipynb+json'], + 'irm' => ['application/vnd.ibm.rights-management'], + 'irp' => ['application/vnd.irepository.package+xml'], + 'iso' => ['application/x-cd-image', 'application/x-gamecube-iso-image', 'application/x-gamecube-rom', 'application/x-iso9660-image', 'application/x-saturn-rom', 'application/x-sega-cd-rom', 'application/x-wbfs', 'application/x-wia', 'application/x-wii-iso-image', 'application/x-wii-rom'], + 'iso9660' => ['application/x-cd-image', 'application/x-iso9660-image'], + 'it' => ['audio/x-it'], + 'it87' => ['application/x-it87'], + 'itp' => ['application/vnd.shana.informed.formtemplate'], + 'ivp' => ['application/vnd.immervision-ivp'], + 'ivu' => ['application/vnd.immervision-ivu'], + 'j2c' => ['image/x-jp2-codestream'], + 'j2k' => ['image/x-jp2-codestream'], + 'jad' => ['text/vnd.sun.j2me.app-descriptor'], + 'jam' => ['application/vnd.jam'], + 'jar' => ['application/x-java-archive', 'application/java-archive', 'application/x-jar'], + 'java' => ['text/x-java', 'text/x-java-source'], + 'jceks' => ['application/x-java-jce-keystore'], + 'jisp' => ['application/vnd.jisp'], + 'jks' => ['application/x-java-keystore'], + 'jlt' => ['application/vnd.hp-jlyt'], + 'jng' => ['image/x-jng'], + 'jnlp' => ['application/x-java-jnlp-file'], + 'joda' => ['application/vnd.joost.joda-archive'], + 'jp2' => ['image/jp2', 'image/jpeg2000', 'image/jpeg2000-image', 'image/x-jpeg2000-image'], + 'jpc' => ['image/x-jp2-codestream'], + 'jpe' => ['image/jpeg', 'image/pjpeg'], + 'jpeg' => ['image/jpeg', 'image/pjpeg'], + 'jpf' => ['image/jpx'], + 'jpg' => ['image/jpeg', 'image/pjpeg'], + 'jpg2' => ['image/jp2', 'image/jpeg2000', 'image/jpeg2000-image', 'image/x-jpeg2000-image'], + 'jpgm' => ['image/jpm', 'video/jpm'], + 'jpgv' => ['video/jpeg'], + 'jpm' => ['image/jpm', 'video/jpm'], + 'jpr' => ['application/x-jbuilder-project'], + 'jpx' => ['application/x-jbuilder-project', 'image/jpx'], + 'jrd' => ['application/jrd+json'], + 'js' => ['text/javascript', 'application/javascript', 'application/x-javascript'], + 'jsm' => ['application/javascript', 'application/x-javascript', 'text/javascript'], + 'json' => ['application/json'], + 'json-patch' => ['application/json-patch+json'], + 'jsonld' => ['application/ld+json'], + 'jsonml' => ['application/jsonml+json'], + 'k25' => ['image/x-kodak-k25'], + 'k7' => ['application/x-thomson-cassette'], + 'kar' => ['audio/midi', 'audio/x-midi'], + 'karbon' => ['application/vnd.kde.karbon', 'application/x-karbon'], + 'kdc' => ['image/x-kodak-kdc'], + 'kdelnk' => ['application/x-desktop', 'application/x-gnome-app-info'], + 'kexi' => ['application/x-kexiproject-sqlite', 'application/x-kexiproject-sqlite2', 'application/x-kexiproject-sqlite3', 'application/x-vnd.kde.kexi'], + 'kexic' => ['application/x-kexi-connectiondata'], + 'kexis' => ['application/x-kexiproject-shortcut'], + 'key' => ['application/vnd.apple.keynote', 'application/x-iwork-keynote-sffkey'], + 'kfo' => ['application/vnd.kde.kformula', 'application/x-kformula'], + 'kia' => ['application/vnd.kidspiration'], + 'kil' => ['application/x-killustrator'], + 'kino' => ['application/smil', 'application/smil+xml'], + 'kml' => ['application/vnd.google-earth.kml+xml'], + 'kmz' => ['application/vnd.google-earth.kmz'], + 'kne' => ['application/vnd.kinar'], + 'knp' => ['application/vnd.kinar'], + 'kon' => ['application/vnd.kde.kontour', 'application/x-kontour'], + 'kpm' => ['application/x-kpovmodeler'], + 'kpr' => ['application/vnd.kde.kpresenter', 'application/x-kpresenter'], + 'kpt' => ['application/vnd.kde.kpresenter', 'application/x-kpresenter'], + 'kpxx' => ['application/vnd.ds-keypoint'], + 'kra' => ['application/x-krita'], + 'ks' => ['application/x-java-keystore'], + 'ksp' => ['application/vnd.kde.kspread', 'application/x-kspread'], + 'ktr' => ['application/vnd.kahootz'], + 'ktx' => ['image/ktx'], + 'ktz' => ['application/vnd.kahootz'], + 'kud' => ['application/x-kugar'], + 'kwd' => ['application/vnd.kde.kword', 'application/x-kword'], + 'kwt' => ['application/vnd.kde.kword', 'application/x-kword'], + 'la' => ['application/x-shared-library-la'], + 'lasxml' => ['application/vnd.las.las+xml'], + 'latex' => ['application/x-latex', 'application/x-tex', 'text/x-tex'], + 'lbd' => ['application/vnd.llamagraphics.life-balance.desktop'], + 'lbe' => ['application/vnd.llamagraphics.life-balance.exchange+xml'], + 'lbm' => ['image/x-iff', 'image/x-ilbm'], + 'ldif' => ['text/x-ldif'], + 'les' => ['application/vnd.hhe.lesson-player'], + 'lha' => ['application/x-lha', 'application/x-lzh-compressed'], + 'lhs' => ['text/x-literate-haskell'], + 'lhz' => ['application/x-lhz'], + 'link66' => ['application/vnd.route66.link66+xml'], + 'list' => ['text/plain'], + 'list3820' => ['application/vnd.ibm.modcap'], + 'listafp' => ['application/vnd.ibm.modcap'], + 'lnk' => ['application/x-ms-shortcut'], + 'lnx' => ['application/x-atari-lynx-rom'], + 'loas' => ['audio/usac'], + 'log' => ['text/plain', 'text/x-log'], + 'lostxml' => ['application/lost+xml'], + 'lrf' => ['application/octet-stream'], + 'lrm' => ['application/vnd.ms-lrm'], + 'lrv' => ['video/mp4', 'video/mp4v-es', 'video/x-m4v'], + 'lrz' => ['application/x-lrzip'], + 'ltf' => ['application/vnd.frogans.ltf'], + 'ltx' => ['application/x-tex', 'text/x-tex'], + 'lua' => ['text/x-lua'], + 'lvp' => ['audio/vnd.lucent.voice'], + 'lwo' => ['image/x-lwo'], + 'lwob' => ['image/x-lwo'], + 'lwp' => ['application/vnd.lotus-wordpro'], + 'lws' => ['image/x-lws'], + 'ly' => ['text/x-lilypond'], + 'lyx' => ['application/x-lyx', 'text/x-lyx'], + 'lz' => ['application/x-lzip'], + 'lz4' => ['application/x-lz4'], + 'lzh' => ['application/x-lha', 'application/x-lzh-compressed'], + 'lzma' => ['application/x-lzma'], + 'lzo' => ['application/x-lzop'], + 'm' => ['text/x-matlab', 'text/x-objcsrc', 'text/x-octave'], + 'm13' => ['application/x-msmediaview'], + 'm14' => ['application/x-msmediaview'], + 'm15' => ['audio/x-mod'], + 'm1u' => ['video/vnd.mpegurl', 'video/x-mpegurl'], + 'm1v' => ['video/mpeg'], + 'm21' => ['application/mp21'], + 'm2a' => ['audio/mpeg'], + 'm2t' => ['video/mp2t'], + 'm2ts' => ['video/mp2t'], + 'm2v' => ['video/mpeg'], + 'm3a' => ['audio/mpeg'], + 'm3u' => ['audio/x-mpegurl', 'application/m3u', 'application/vnd.apple.mpegurl', 'audio/m3u', 'audio/mpegurl', 'audio/x-m3u', 'audio/x-mp3-playlist'], + 'm3u8' => ['application/m3u', 'application/vnd.apple.mpegurl', 'audio/m3u', 'audio/mpegurl', 'audio/x-m3u', 'audio/x-mp3-playlist', 'audio/x-mpegurl'], + 'm4' => ['application/x-m4'], + 'm4a' => ['audio/mp4', 'audio/m4a', 'audio/x-m4a'], + 'm4b' => ['audio/x-m4b'], + 'm4r' => ['audio/x-m4r'], + 'm4u' => ['video/vnd.mpegurl', 'video/x-mpegurl'], + 'm4v' => ['video/mp4', 'video/mp4v-es', 'video/x-m4v'], + 'm7' => ['application/x-thomson-cartridge-memo7'], + 'ma' => ['application/mathematica'], + 'mab' => ['application/x-markaby'], + 'mads' => ['application/mads+xml'], + 'mag' => ['application/vnd.ecowin.chart'], + 'mak' => ['text/x-makefile'], + 'maker' => ['application/vnd.framemaker'], + 'man' => ['application/x-troff-man', 'text/troff'], + 'manifest' => ['text/cache-manifest'], + 'mar' => ['application/octet-stream'], + 'markdown' => ['text/markdown', 'text/x-markdown'], + 'mathml' => ['application/mathml+xml'], + 'mb' => ['application/mathematica'], + 'mbk' => ['application/vnd.mobius.mbk'], + 'mbox' => ['application/mbox'], + 'mc1' => ['application/vnd.medcalcdata'], + 'mcd' => ['application/vnd.mcd'], + 'mcurl' => ['text/vnd.curl.mcurl'], + 'md' => ['text/markdown', 'text/x-markdown'], + 'mdb' => ['application/x-msaccess', 'application/mdb', 'application/msaccess', 'application/vnd.ms-access', 'application/vnd.msaccess', 'application/x-mdb', 'zz-application/zz-winassoc-mdb'], + 'mdi' => ['image/vnd.ms-modi'], + 'mdx' => ['application/x-genesis-32x-rom'], + 'me' => ['text/troff', 'text/x-troff-me'], + 'med' => ['audio/x-mod'], + 'mesh' => ['model/mesh'], + 'meta4' => ['application/metalink4+xml'], + 'metalink' => ['application/metalink+xml'], + 'mets' => ['application/mets+xml'], + 'mfm' => ['application/vnd.mfmp'], + 'mft' => ['application/rpki-manifest'], + 'mgp' => ['application/vnd.osgeo.mapguide.package', 'application/x-magicpoint'], + 'mgz' => ['application/vnd.proteus.magazine'], + 'mht' => ['application/x-mimearchive'], + 'mhtml' => ['application/x-mimearchive'], + 'mid' => ['audio/midi', 'audio/x-midi'], + 'midi' => ['audio/midi', 'audio/x-midi'], + 'mie' => ['application/x-mie'], + 'mif' => ['application/vnd.mif', 'application/x-mif'], + 'mime' => ['message/rfc822'], + 'minipsf' => ['audio/x-minipsf'], + 'mj2' => ['video/mj2'], + 'mjp2' => ['video/mj2'], + 'mjpeg' => ['video/x-mjpeg'], + 'mjpg' => ['video/x-mjpeg'], + 'mjs' => ['application/javascript', 'application/x-javascript', 'text/javascript'], + 'mk' => ['text/x-makefile'], + 'mk3d' => ['video/x-matroska', 'video/x-matroska-3d'], + 'mka' => ['audio/x-matroska'], + 'mkd' => ['text/markdown', 'text/x-markdown'], + 'mks' => ['video/x-matroska'], + 'mkv' => ['video/x-matroska'], + 'ml' => ['text/x-ocaml'], + 'mli' => ['text/x-ocaml'], + 'mlp' => ['application/vnd.dolby.mlp'], + 'mm' => ['text/x-troff-mm'], + 'mmd' => ['application/vnd.chipnuts.karaoke-mmd'], + 'mmf' => ['application/vnd.smaf', 'application/x-smaf'], + 'mml' => ['application/mathml+xml', 'text/mathml'], + 'mmr' => ['image/vnd.fujixerox.edmics-mmr'], + 'mng' => ['video/x-mng'], + 'mny' => ['application/x-msmoney'], + 'mo' => ['application/x-gettext-translation', 'text/x-modelica'], + 'mo3' => ['audio/x-mo3'], + 'mobi' => ['application/x-mobipocket-ebook'], + 'moc' => ['text/x-moc'], + 'mod' => ['audio/x-mod'], + 'mods' => ['application/mods+xml'], + 'mof' => ['text/x-mof'], + 'moov' => ['video/quicktime'], + 'mount' => ['text/x-systemd-unit'], + 'mov' => ['video/quicktime'], + 'movie' => ['video/x-sgi-movie'], + 'mp+' => ['audio/x-musepack'], + 'mp2' => ['audio/mp2', 'audio/mpeg', 'audio/x-mp2', 'video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'], + 'mp21' => ['application/mp21'], + 'mp2a' => ['audio/mpeg'], + 'mp3' => ['audio/mpeg', 'audio/mp3', 'audio/x-mp3', 'audio/x-mpeg', 'audio/x-mpg'], + 'mp4' => ['video/mp4', 'video/mp4v-es', 'video/x-m4v'], + 'mp4a' => ['audio/mp4'], + 'mp4s' => ['application/mp4'], + 'mp4v' => ['video/mp4'], + 'mpc' => ['application/vnd.mophun.certificate', 'audio/x-musepack'], + 'mpe' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'], + 'mpeg' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'], + 'mpg' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'], + 'mpg4' => ['video/mp4'], + 'mpga' => ['audio/mp3', 'audio/mpeg', 'audio/x-mp3', 'audio/x-mpeg', 'audio/x-mpg'], + 'mpkg' => ['application/vnd.apple.installer+xml'], + 'mpl' => ['video/mp2t'], + 'mpls' => ['video/mp2t'], + 'mpm' => ['application/vnd.blueice.multipass'], + 'mpn' => ['application/vnd.mophun.application'], + 'mpp' => ['application/vnd.ms-project', 'audio/x-musepack'], + 'mpt' => ['application/vnd.ms-project'], + 'mpy' => ['application/vnd.ibm.minipay'], + 'mqy' => ['application/vnd.mobius.mqy'], + 'mrc' => ['application/marc'], + 'mrcx' => ['application/marcxml+xml'], + 'mrl' => ['text/x-mrml'], + 'mrml' => ['text/x-mrml'], + 'mrw' => ['image/x-minolta-mrw'], + 'ms' => ['text/troff', 'text/x-troff-ms'], + 'mscml' => ['application/mediaservercontrol+xml'], + 'mseed' => ['application/vnd.fdsn.mseed'], + 'mseq' => ['application/vnd.mseq'], + 'msf' => ['application/vnd.epson.msf'], + 'msh' => ['model/mesh'], + 'msi' => ['application/x-msdownload', 'application/x-msi'], + 'msl' => ['application/vnd.mobius.msl'], + 'msod' => ['image/x-msod'], + 'msty' => ['application/vnd.muvee.style'], + 'msx' => ['application/x-msx-rom'], + 'mtm' => ['audio/x-mod'], + 'mts' => ['model/vnd.mts', 'video/mp2t'], + 'mup' => ['text/x-mup'], + 'mus' => ['application/vnd.musician'], + 'musicxml' => ['application/vnd.recordare.musicxml+xml'], + 'mvb' => ['application/x-msmediaview'], + 'mwf' => ['application/vnd.mfer'], + 'mxf' => ['application/mxf'], + 'mxl' => ['application/vnd.recordare.musicxml'], + 'mxml' => ['application/xv+xml'], + 'mxs' => ['application/vnd.triscape.mxs'], + 'mxu' => ['video/vnd.mpegurl', 'video/x-mpegurl'], + 'n-gage' => ['application/vnd.nokia.n-gage.symbian.install'], + 'n3' => ['text/n3'], + 'n64' => ['application/x-n64-rom'], + 'nb' => ['application/mathematica', 'application/x-mathematica'], + 'nbp' => ['application/vnd.wolfram.player'], + 'nc' => ['application/x-netcdf'], + 'ncx' => ['application/x-dtbncx+xml'], + 'nds' => ['application/x-nintendo-ds-rom'], + 'nef' => ['image/x-nikon-nef'], + 'nes' => ['application/x-nes-rom'], + 'nez' => ['application/x-nes-rom'], + 'nfo' => ['text/x-nfo'], + 'ngc' => ['application/x-neo-geo-pocket-color-rom'], + 'ngdat' => ['application/vnd.nokia.n-gage.data'], + 'ngp' => ['application/x-neo-geo-pocket-rom'], + 'nitf' => ['application/vnd.nitf'], + 'nlu' => ['application/vnd.neurolanguage.nlu'], + 'nml' => ['application/vnd.enliven'], + 'nnd' => ['application/vnd.noblenet-directory'], + 'nns' => ['application/vnd.noblenet-sealer'], + 'nnw' => ['application/vnd.noblenet-web'], + 'not' => ['text/x-mup'], + 'npx' => ['image/vnd.net-fpx'], + 'nsc' => ['application/x-conference', 'application/x-netshow-channel'], + 'nsf' => ['application/vnd.lotus-notes'], + 'nsv' => ['video/x-nsv'], + 'ntf' => ['application/vnd.nitf'], + 'nzb' => ['application/x-nzb'], + 'o' => ['application/x-object'], + 'oa2' => ['application/vnd.fujitsu.oasys2'], + 'oa3' => ['application/vnd.fujitsu.oasys3'], + 'oas' => ['application/vnd.fujitsu.oasys'], + 'obd' => ['application/x-msbinder'], + 'obj' => ['application/x-tgif'], + 'ocl' => ['text/x-ocl'], + 'oda' => ['application/oda'], + 'odb' => ['application/vnd.oasis.opendocument.database', 'application/vnd.sun.xml.base'], + 'odc' => ['application/vnd.oasis.opendocument.chart'], + 'odf' => ['application/vnd.oasis.opendocument.formula'], + 'odft' => ['application/vnd.oasis.opendocument.formula-template'], + 'odg' => ['application/vnd.oasis.opendocument.graphics'], + 'odi' => ['application/vnd.oasis.opendocument.image'], + 'odm' => ['application/vnd.oasis.opendocument.text-master'], + 'odp' => ['application/vnd.oasis.opendocument.presentation'], + 'ods' => ['application/vnd.oasis.opendocument.spreadsheet'], + 'odt' => ['application/vnd.oasis.opendocument.text'], + 'oga' => ['audio/ogg', 'audio/vorbis', 'audio/x-flac+ogg', 'audio/x-ogg', 'audio/x-oggflac', 'audio/x-speex+ogg', 'audio/x-vorbis', 'audio/x-vorbis+ogg'], + 'ogg' => ['audio/ogg', 'audio/vorbis', 'audio/x-flac+ogg', 'audio/x-ogg', 'audio/x-oggflac', 'audio/x-speex+ogg', 'audio/x-vorbis', 'audio/x-vorbis+ogg', 'video/ogg', 'video/x-ogg', 'video/x-theora', 'video/x-theora+ogg'], + 'ogm' => ['video/x-ogm', 'video/x-ogm+ogg'], + 'ogv' => ['video/ogg', 'video/x-ogg'], + 'ogx' => ['application/ogg', 'application/x-ogg'], + 'old' => ['application/x-trash'], + 'oleo' => ['application/x-oleo'], + 'omdoc' => ['application/omdoc+xml'], + 'onepkg' => ['application/onenote'], + 'onetmp' => ['application/onenote'], + 'onetoc' => ['application/onenote'], + 'onetoc2' => ['application/onenote'], + 'ooc' => ['text/x-ooc'], + 'opf' => ['application/oebps-package+xml'], + 'opml' => ['text/x-opml', 'text/x-opml+xml'], + 'oprc' => ['application/vnd.palm', 'application/x-palm-database'], + 'opus' => ['audio/ogg', 'audio/x-ogg', 'audio/x-opus+ogg'], + 'ora' => ['image/openraster'], + 'orf' => ['image/x-olympus-orf'], + 'org' => ['application/vnd.lotus-organizer'], + 'osf' => ['application/vnd.yamaha.openscoreformat'], + 'osfpvg' => ['application/vnd.yamaha.openscoreformat.osfpvg+xml'], + 'otc' => ['application/vnd.oasis.opendocument.chart-template'], + 'otf' => ['application/vnd.oasis.opendocument.formula-template', 'application/x-font-otf', 'font/otf'], + 'otg' => ['application/vnd.oasis.opendocument.graphics-template'], + 'oth' => ['application/vnd.oasis.opendocument.text-web'], + 'oti' => ['application/vnd.oasis.opendocument.image-template'], + 'otp' => ['application/vnd.oasis.opendocument.presentation-template'], + 'ots' => ['application/vnd.oasis.opendocument.spreadsheet-template'], + 'ott' => ['application/vnd.oasis.opendocument.text-template'], + 'owl' => ['application/rdf+xml', 'text/rdf'], + 'owx' => ['application/owl+xml'], + 'oxps' => ['application/oxps', 'application/vnd.ms-xpsdocument', 'application/xps'], + 'oxt' => ['application/vnd.openofficeorg.extension'], + 'p' => ['text/x-pascal'], + 'p10' => ['application/pkcs10'], + 'p12' => ['application/pkcs12', 'application/x-pkcs12'], + 'p65' => ['application/x-pagemaker'], + 'p7b' => ['application/x-pkcs7-certificates'], + 'p7c' => ['application/pkcs7-mime'], + 'p7m' => ['application/pkcs7-mime'], + 'p7r' => ['application/x-pkcs7-certreqresp'], + 'p7s' => ['application/pkcs7-signature'], + 'p8' => ['application/pkcs8'], + 'p8e' => ['application/pkcs8-encrypted'], + 'pack' => ['application/x-java-pack200'], + 'pak' => ['application/x-pak'], + 'par2' => ['application/x-par2'], + 'part' => ['application/x-partial-download'], + 'pas' => ['text/x-pascal'], + 'pat' => ['image/x-gimp-pat'], + 'patch' => ['text/x-diff', 'text/x-patch'], + 'path' => ['text/x-systemd-unit'], + 'paw' => ['application/vnd.pawaafile'], + 'pbd' => ['application/vnd.powerbuilder6'], + 'pbm' => ['image/x-portable-bitmap'], + 'pcap' => ['application/pcap', 'application/vnd.tcpdump.pcap', 'application/x-pcap'], + 'pcd' => ['image/x-photo-cd'], + 'pce' => ['application/x-pc-engine-rom'], + 'pcf' => ['application/x-cisco-vpn-settings', 'application/x-font-pcf'], + 'pcf.Z' => ['application/x-font-pcf'], + 'pcf.gz' => ['application/x-font-pcf'], + 'pcl' => ['application/vnd.hp-pcl'], + 'pclxl' => ['application/vnd.hp-pclxl'], + 'pct' => ['image/x-pict'], + 'pcurl' => ['application/vnd.curl.pcurl'], + 'pcx' => ['image/vnd.zbrush.pcx', 'image/x-pcx'], + 'pdb' => ['application/vnd.palm', 'application/x-aportisdoc', 'application/x-palm-database'], + 'pdc' => ['application/x-aportisdoc'], + 'pdf' => ['application/pdf', 'application/acrobat', 'application/nappdf', 'application/x-pdf', 'image/pdf'], + 'pdf.bz2' => ['application/x-bzpdf'], + 'pdf.gz' => ['application/x-gzpdf'], + 'pdf.lz' => ['application/x-lzpdf'], + 'pdf.xz' => ['application/x-xzpdf'], + 'pef' => ['image/x-pentax-pef'], + 'pem' => ['application/x-x509-ca-cert'], + 'perl' => ['application/x-perl', 'text/x-perl'], + 'pfa' => ['application/x-font-type1'], + 'pfb' => ['application/x-font-type1'], + 'pfm' => ['application/x-font-type1'], + 'pfr' => ['application/font-tdpfr'], + 'pfx' => ['application/pkcs12', 'application/x-pkcs12'], + 'pgm' => ['image/x-portable-graymap'], + 'pgn' => ['application/vnd.chess-pgn', 'application/x-chess-pgn'], + 'pgp' => ['application/pgp', 'application/pgp-encrypted', 'application/pgp-keys', 'application/pgp-signature'], + 'php' => ['application/x-php'], + 'php3' => ['application/x-php'], + 'php4' => ['application/x-php'], + 'php5' => ['application/x-php'], + 'phps' => ['application/x-php'], + 'pic' => ['image/x-pict'], + 'pict' => ['image/x-pict'], + 'pict1' => ['image/x-pict'], + 'pict2' => ['image/x-pict'], + 'pk' => ['application/x-tex-pk'], + 'pkg' => ['application/octet-stream', 'application/x-xar'], + 'pki' => ['application/pkixcmp'], + 'pkipath' => ['application/pkix-pkipath'], + 'pkr' => ['application/pgp-keys'], + 'pl' => ['application/x-perl', 'text/x-perl'], + 'pla' => ['audio/x-iriver-pla'], + 'plb' => ['application/vnd.3gpp.pic-bw-large'], + 'plc' => ['application/vnd.mobius.plc'], + 'plf' => ['application/vnd.pocketlearn'], + 'pln' => ['application/x-planperfect'], + 'pls' => ['application/pls', 'application/pls+xml', 'audio/scpls', 'audio/x-scpls'], + 'pm' => ['application/x-pagemaker', 'application/x-perl', 'text/x-perl'], + 'pm6' => ['application/x-pagemaker'], + 'pmd' => ['application/x-pagemaker'], + 'pml' => ['application/vnd.ctc-posml'], + 'png' => ['image/png'], + 'pnm' => ['image/x-portable-anymap'], + 'pntg' => ['image/x-macpaint'], + 'po' => ['application/x-gettext', 'text/x-gettext-translation', 'text/x-po'], + 'pod' => ['application/x-perl', 'text/x-perl'], + 'por' => ['application/x-spss-por'], + 'portpkg' => ['application/vnd.macports.portpkg'], + 'pot' => ['application/mspowerpoint', 'application/powerpoint', 'application/vnd.ms-powerpoint', 'application/x-mspowerpoint', 'text/x-gettext-translation-template', 'text/x-pot'], + 'potm' => ['application/vnd.ms-powerpoint.template.macroenabled.12'], + 'potx' => ['application/vnd.openxmlformats-officedocument.presentationml.template'], + 'ppam' => ['application/vnd.ms-powerpoint.addin.macroenabled.12'], + 'ppd' => ['application/vnd.cups-ppd'], + 'ppm' => ['image/x-portable-pixmap'], + 'pps' => ['application/mspowerpoint', 'application/powerpoint', 'application/vnd.ms-powerpoint', 'application/x-mspowerpoint'], + 'ppsm' => ['application/vnd.ms-powerpoint.slideshow.macroenabled.12'], + 'ppsx' => ['application/vnd.openxmlformats-officedocument.presentationml.slideshow'], + 'ppt' => ['application/vnd.ms-powerpoint', 'application/mspowerpoint', 'application/powerpoint', 'application/x-mspowerpoint'], + 'pptm' => ['application/vnd.ms-powerpoint.presentation.macroenabled.12'], + 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'], + 'ppz' => ['application/mspowerpoint', 'application/powerpoint', 'application/vnd.ms-powerpoint', 'application/x-mspowerpoint'], + 'pqa' => ['application/vnd.palm', 'application/x-palm-database'], + 'prc' => ['application/vnd.palm', 'application/x-mobipocket-ebook', 'application/x-palm-database'], + 'pre' => ['application/vnd.lotus-freelance'], + 'prf' => ['application/pics-rules'], + 'ps' => ['application/postscript'], + 'ps.bz2' => ['application/x-bzpostscript'], + 'ps.gz' => ['application/x-gzpostscript'], + 'psb' => ['application/vnd.3gpp.pic-bw-small'], + 'psd' => ['application/photoshop', 'application/x-photoshop', 'image/photoshop', 'image/psd', 'image/vnd.adobe.photoshop', 'image/x-photoshop', 'image/x-psd'], + 'psf' => ['application/x-font-linux-psf', 'audio/x-psf'], + 'psf.gz' => ['application/x-gz-font-linux-psf'], + 'psflib' => ['audio/x-psflib'], + 'psid' => ['audio/prs.sid'], + 'pskcxml' => ['application/pskc+xml'], + 'psw' => ['application/x-pocket-word'], + 'ptid' => ['application/vnd.pvi.ptid1'], + 'pub' => ['application/vnd.ms-publisher', 'application/x-mspublisher'], + 'pvb' => ['application/vnd.3gpp.pic-bw-var'], + 'pw' => ['application/x-pw'], + 'pwn' => ['application/vnd.3m.post-it-notes'], + 'py' => ['text/x-python', 'text/x-python3'], + 'py3' => ['text/x-python3'], + 'py3x' => ['text/x-python3'], + 'pya' => ['audio/vnd.ms-playready.media.pya'], + 'pyc' => ['application/x-python-bytecode'], + 'pyo' => ['application/x-python-bytecode'], + 'pyv' => ['video/vnd.ms-playready.media.pyv'], + 'pyx' => ['text/x-python'], + 'qam' => ['application/vnd.epson.quickanime'], + 'qbo' => ['application/vnd.intu.qbo'], + 'qd' => ['application/x-fd-file', 'application/x-raw-floppy-disk-image'], + 'qfx' => ['application/vnd.intu.qfx'], + 'qif' => ['application/x-qw', 'image/x-quicktime'], + 'qml' => ['text/x-qml'], + 'qmlproject' => ['text/x-qml'], + 'qmltypes' => ['text/x-qml'], + 'qp' => ['application/x-qpress'], + 'qps' => ['application/vnd.publishare-delta-tree'], + 'qt' => ['video/quicktime'], + 'qti' => ['application/x-qtiplot'], + 'qti.gz' => ['application/x-qtiplot'], + 'qtif' => ['image/x-quicktime'], + 'qtl' => ['application/x-quicktime-media-link', 'application/x-quicktimeplayer'], + 'qtvr' => ['video/quicktime'], + 'qwd' => ['application/vnd.quark.quarkxpress'], + 'qwt' => ['application/vnd.quark.quarkxpress'], + 'qxb' => ['application/vnd.quark.quarkxpress'], + 'qxd' => ['application/vnd.quark.quarkxpress'], + 'qxl' => ['application/vnd.quark.quarkxpress'], + 'qxt' => ['application/vnd.quark.quarkxpress'], + 'ra' => ['audio/vnd.m-realaudio', 'audio/vnd.rn-realaudio', 'audio/x-pn-realaudio'], + 'raf' => ['image/x-fuji-raf'], + 'ram' => ['application/ram', 'audio/x-pn-realaudio'], + 'raml' => ['application/raml+yaml'], + 'rar' => ['application/x-rar-compressed', 'application/vnd.rar', 'application/x-rar'], + 'ras' => ['image/x-cmu-raster'], + 'raw' => ['image/x-panasonic-raw', 'image/x-panasonic-rw'], + 'raw-disk-image' => ['application/x-raw-disk-image'], + 'raw-disk-image.xz' => ['application/x-raw-disk-image-xz-compressed'], + 'rax' => ['audio/vnd.m-realaudio', 'audio/vnd.rn-realaudio', 'audio/x-pn-realaudio'], + 'rb' => ['application/x-ruby'], + 'rcprofile' => ['application/vnd.ipunplugged.rcprofile'], + 'rdf' => ['application/rdf+xml', 'text/rdf'], + 'rdfs' => ['application/rdf+xml', 'text/rdf'], + 'rdz' => ['application/vnd.data-vision.rdz'], + 'reg' => ['text/x-ms-regedit'], + 'rej' => ['application/x-reject', 'text/x-reject'], + 'rep' => ['application/vnd.businessobjects'], + 'res' => ['application/x-dtbresource+xml'], + 'rgb' => ['image/x-rgb'], + 'rif' => ['application/reginfo+xml'], + 'rip' => ['audio/vnd.rip'], + 'ris' => ['application/x-research-info-systems'], + 'rl' => ['application/resource-lists+xml'], + 'rlc' => ['image/vnd.fujixerox.edmics-rlc'], + 'rld' => ['application/resource-lists-diff+xml'], + 'rle' => ['image/rle'], + 'rm' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'], + 'rmi' => ['audio/midi'], + 'rmj' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'], + 'rmm' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'], + 'rmp' => ['audio/x-pn-realaudio-plugin'], + 'rms' => ['application/vnd.jcp.javame.midlet-rms', 'application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'], + 'rmvb' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'], + 'rmx' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'], + 'rnc' => ['application/relax-ng-compact-syntax', 'application/x-rnc'], + 'rng' => ['application/xml', 'text/xml'], + 'roa' => ['application/rpki-roa'], + 'roff' => ['application/x-troff', 'text/troff', 'text/x-troff'], + 'rp' => ['image/vnd.rn-realpix'], + 'rp9' => ['application/vnd.cloanto.rp9'], + 'rpm' => ['application/x-redhat-package-manager', 'application/x-rpm'], + 'rpss' => ['application/vnd.nokia.radio-presets'], + 'rpst' => ['application/vnd.nokia.radio-preset'], + 'rq' => ['application/sparql-query'], + 'rs' => ['application/rls-services+xml', 'text/rust'], + 'rsd' => ['application/rsd+xml'], + 'rss' => ['application/rss+xml', 'text/rss'], + 'rt' => ['text/vnd.rn-realtext'], + 'rtf' => ['application/rtf', 'text/rtf'], + 'rtx' => ['text/richtext'], + 'rv' => ['video/vnd.rn-realvideo', 'video/x-real-video'], + 'rvx' => ['video/vnd.rn-realvideo', 'video/x-real-video'], + 'rw2' => ['image/x-panasonic-raw2', 'image/x-panasonic-rw2'], + 's' => ['text/x-asm'], + 's3m' => ['audio/s3m', 'audio/x-s3m'], + 'saf' => ['application/vnd.yamaha.smaf-audio'], + 'sam' => ['application/x-amipro'], + 'sami' => ['application/x-sami'], + 'sap' => ['application/x-sap-file', 'application/x-thomson-sap-image'], + 'sass' => ['text/x-sass'], + 'sav' => ['application/x-spss-sav', 'application/x-spss-savefile'], + 'sbml' => ['application/sbml+xml'], + 'sc' => ['application/vnd.ibm.secure-container'], + 'scala' => ['text/x-scala'], + 'scd' => ['application/x-msschedule'], + 'scm' => ['application/vnd.lotus-screencam', 'text/x-scheme'], + 'scope' => ['text/x-systemd-unit'], + 'scq' => ['application/scvp-cv-request'], + 'scs' => ['application/scvp-cv-response'], + 'scss' => ['text/x-scss'], + 'scurl' => ['text/vnd.curl.scurl'], + 'sda' => ['application/vnd.stardivision.draw'], + 'sdc' => ['application/vnd.stardivision.calc'], + 'sdd' => ['application/vnd.stardivision.impress'], + 'sdkd' => ['application/vnd.solent.sdkm+xml'], + 'sdkm' => ['application/vnd.solent.sdkm+xml'], + 'sdp' => ['application/sdp', 'application/vnd.sdp', 'application/vnd.stardivision.impress', 'application/x-sdp'], + 'sds' => ['application/vnd.stardivision.chart'], + 'sdw' => ['application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global'], + 'see' => ['application/vnd.seemail'], + 'seed' => ['application/vnd.fdsn.seed'], + 'sema' => ['application/vnd.sema'], + 'semd' => ['application/vnd.semd'], + 'semf' => ['application/vnd.semf'], + 'ser' => ['application/java-serialized-object'], + 'service' => ['text/x-dbus-service', 'text/x-systemd-unit'], + 'setpay' => ['application/set-payment-initiation'], + 'setreg' => ['application/set-registration-initiation'], + 'sfc' => ['application/vnd.nintendo.snes.rom', 'application/x-snes-rom'], + 'sfd-hdstx' => ['application/vnd.hydrostatix.sof-data'], + 'sfs' => ['application/vnd.spotfire.sfs'], + 'sfv' => ['text/x-sfv'], + 'sg' => ['application/x-sg1000-rom'], + 'sgb' => ['application/x-gameboy-rom'], + 'sgf' => ['application/x-go-sgf'], + 'sgi' => ['image/sgi', 'image/x-sgi'], + 'sgl' => ['application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global'], + 'sgm' => ['text/sgml'], + 'sgml' => ['text/sgml'], + 'sh' => ['application/x-sh', 'application/x-shellscript', 'text/x-sh'], + 'shape' => ['application/x-dia-shape'], + 'shar' => ['application/x-shar'], + 'shf' => ['application/shf+xml'], + 'shn' => ['application/x-shorten', 'audio/x-shorten'], + 'siag' => ['application/x-siag'], + 'sid' => ['audio/prs.sid', 'image/x-mrsid-image'], + 'sig' => ['application/pgp-signature'], + 'sik' => ['application/x-trash'], + 'sil' => ['audio/silk'], + 'silo' => ['model/mesh'], + 'sis' => ['application/vnd.symbian.install'], + 'sisx' => ['application/vnd.symbian.install', 'x-epoc/x-sisx-app'], + 'sit' => ['application/x-stuffit', 'application/stuffit', 'application/x-sit'], + 'sitx' => ['application/x-stuffitx'], + 'siv' => ['application/sieve'], + 'sk' => ['image/x-skencil'], + 'sk1' => ['image/x-skencil'], + 'skd' => ['application/vnd.koan'], + 'skm' => ['application/vnd.koan'], + 'skp' => ['application/vnd.koan'], + 'skr' => ['application/pgp-keys'], + 'skt' => ['application/vnd.koan'], + 'sldm' => ['application/vnd.ms-powerpoint.slide.macroenabled.12'], + 'sldx' => ['application/vnd.openxmlformats-officedocument.presentationml.slide'], + 'slice' => ['text/x-systemd-unit'], + 'slk' => ['text/spreadsheet'], + 'slt' => ['application/vnd.epson.salt'], + 'sm' => ['application/vnd.stepmania.stepchart'], + 'smaf' => ['application/vnd.smaf', 'application/x-smaf'], + 'smc' => ['application/vnd.nintendo.snes.rom', 'application/x-snes-rom'], + 'smd' => ['application/vnd.stardivision.mail', 'application/x-genesis-rom'], + 'smf' => ['application/vnd.stardivision.math'], + 'smi' => ['application/smil', 'application/smil+xml', 'application/x-sami'], + 'smil' => ['application/smil', 'application/smil+xml'], + 'sml' => ['application/smil', 'application/smil+xml'], + 'sms' => ['application/x-sms-rom'], + 'smv' => ['video/x-smv'], + 'smzip' => ['application/vnd.stepmania.package'], + 'snap' => ['application/vnd.snap'], + 'snd' => ['audio/basic'], + 'snf' => ['application/x-font-snf'], + 'so' => ['application/octet-stream', 'application/x-sharedlib'], + 'socket' => ['text/x-systemd-unit'], + 'spc' => ['application/x-pkcs7-certificates'], + 'spd' => ['application/x-font-speedo'], + 'spec' => ['text/x-rpm-spec'], + 'spf' => ['application/vnd.yamaha.smaf-phrase'], + 'spl' => ['application/futuresplash', 'application/vnd.adobe.flash.movie', 'application/x-futuresplash', 'application/x-shockwave-flash'], + 'spm' => ['application/x-source-rpm'], + 'spot' => ['text/vnd.in3d.spot'], + 'spp' => ['application/scvp-vp-response'], + 'spq' => ['application/scvp-vp-request'], + 'spx' => ['audio/ogg', 'audio/x-speex'], + 'sql' => ['application/sql', 'application/x-sql', 'text/x-sql'], + 'sqlite2' => ['application/x-sqlite2'], + 'sqlite3' => ['application/vnd.sqlite3', 'application/x-sqlite3'], + 'sqsh' => ['application/vnd.squashfs'], + 'sr2' => ['image/x-sony-sr2'], + 'src' => ['application/x-wais-source'], + 'src.rpm' => ['application/x-source-rpm'], + 'srf' => ['image/x-sony-srf'], + 'srt' => ['application/x-srt', 'application/x-subrip'], + 'sru' => ['application/sru+xml'], + 'srx' => ['application/sparql-results+xml'], + 'ss' => ['text/x-scheme'], + 'ssa' => ['text/x-ssa'], + 'ssdl' => ['application/ssdl+xml'], + 'sse' => ['application/vnd.kodak-descriptor'], + 'ssf' => ['application/vnd.epson.ssf'], + 'ssml' => ['application/ssml+xml'], + 'st' => ['application/vnd.sailingtracker.track'], + 'stc' => ['application/vnd.sun.xml.calc.template'], + 'std' => ['application/vnd.sun.xml.draw.template'], + 'stf' => ['application/vnd.wt.stf'], + 'sti' => ['application/vnd.sun.xml.impress.template'], + 'stk' => ['application/hyperstudio'], + 'stl' => ['application/vnd.ms-pki.stl', 'model/stl', 'model/x.stl-ascii', 'model/x.stl-binary'], + 'stm' => ['audio/x-stm'], + 'str' => ['application/vnd.pg.format'], + 'stw' => ['application/vnd.sun.xml.writer.template'], + 'sty' => ['application/x-tex', 'text/x-tex'], + 'sub' => ['image/vnd.dvb.subtitle', 'text/vnd.dvb.subtitle', 'text/x-microdvd', 'text/x-mpsub', 'text/x-subviewer'], + 'sun' => ['image/x-sun-raster'], + 'sus' => ['application/vnd.sus-calendar'], + 'susp' => ['application/vnd.sus-calendar'], + 'sv' => ['text/x-svsrc'], + 'sv4cpio' => ['application/x-sv4cpio'], + 'sv4crc' => ['application/x-sv4crc'], + 'svc' => ['application/vnd.dvb.service'], + 'svd' => ['application/vnd.svd'], + 'svg' => ['image/svg+xml', 'image/svg'], + 'svgz' => ['image/svg+xml', 'image/svg+xml-compressed'], + 'svh' => ['text/x-svhdr'], + 'swa' => ['application/x-director'], + 'swap' => ['text/x-systemd-unit'], + 'swf' => ['application/futuresplash', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash'], + 'swi' => ['application/vnd.aristanetworks.swi'], + 'swm' => ['application/x-ms-wim'], + 'sxc' => ['application/vnd.sun.xml.calc'], + 'sxd' => ['application/vnd.sun.xml.draw'], + 'sxg' => ['application/vnd.sun.xml.writer.global'], + 'sxi' => ['application/vnd.sun.xml.impress'], + 'sxm' => ['application/vnd.sun.xml.math'], + 'sxw' => ['application/vnd.sun.xml.writer'], + 'sylk' => ['text/spreadsheet'], + 't' => ['application/x-perl', 'application/x-troff', 'text/troff', 'text/x-perl', 'text/x-troff'], + 't2t' => ['text/x-txt2tags'], + 't3' => ['application/x-t3vm-image'], + 'taglet' => ['application/vnd.mynfc'], + 'tao' => ['application/vnd.tao.intent-module-archive'], + 'tar' => ['application/x-tar', 'application/x-gtar'], + 'tar.Z' => ['application/x-tarz'], + 'tar.bz' => ['application/x-bzip-compressed-tar'], + 'tar.bz2' => ['application/x-bzip-compressed-tar'], + 'tar.gz' => ['application/x-compressed-tar'], + 'tar.lrz' => ['application/x-lrzip-compressed-tar'], + 'tar.lz' => ['application/x-lzip-compressed-tar'], + 'tar.lz4' => ['application/x-lz4-compressed-tar'], + 'tar.lzma' => ['application/x-lzma-compressed-tar'], + 'tar.lzo' => ['application/x-tzo'], + 'tar.xz' => ['application/x-xz-compressed-tar'], + 'target' => ['text/x-systemd-unit'], + 'taz' => ['application/x-tarz'], + 'tb2' => ['application/x-bzip-compressed-tar'], + 'tbz' => ['application/x-bzip-compressed-tar'], + 'tbz2' => ['application/x-bzip-compressed-tar'], + 'tcap' => ['application/vnd.3gpp2.tcap'], + 'tcl' => ['application/x-tcl', 'text/x-tcl'], + 'teacher' => ['application/vnd.smart.teacher'], + 'tei' => ['application/tei+xml'], + 'teicorpus' => ['application/tei+xml'], + 'tex' => ['application/x-tex', 'text/x-tex'], + 'texi' => ['application/x-texinfo', 'text/x-texinfo'], + 'texinfo' => ['application/x-texinfo', 'text/x-texinfo'], + 'text' => ['text/plain'], + 'tfi' => ['application/thraud+xml'], + 'tfm' => ['application/x-tex-tfm'], + 'tga' => ['image/x-icb', 'image/x-tga'], + 'tgz' => ['application/x-compressed-tar'], + 'theme' => ['application/x-theme'], + 'themepack' => ['application/x-windows-themepack'], + 'thmx' => ['application/vnd.ms-officetheme'], + 'tif' => ['image/tiff'], + 'tiff' => ['image/tiff'], + 'timer' => ['text/x-systemd-unit'], + 'tk' => ['text/x-tcl'], + 'tlrz' => ['application/x-lrzip-compressed-tar'], + 'tlz' => ['application/x-lzma-compressed-tar'], + 'tmo' => ['application/vnd.tmobile-livetv'], + 'tnef' => ['application/ms-tnef', 'application/vnd.ms-tnef'], + 'tnf' => ['application/ms-tnef', 'application/vnd.ms-tnef'], + 'toc' => ['application/x-cdrdao-toc'], + 'torrent' => ['application/x-bittorrent'], + 'tpic' => ['image/x-icb', 'image/x-tga'], + 'tpl' => ['application/vnd.groove-tool-template'], + 'tpt' => ['application/vnd.trid.tpt'], + 'tr' => ['application/x-troff', 'text/troff', 'text/x-troff'], + 'tra' => ['application/vnd.trueapp'], + 'trig' => ['application/trig', 'application/x-trig'], + 'trm' => ['application/x-msterminal'], + 'ts' => ['application/x-linguist', 'text/vnd.qt.linguist', 'text/vnd.trolltech.linguist', 'video/mp2t'], + 'tsd' => ['application/timestamped-data'], + 'tsv' => ['text/tab-separated-values'], + 'tta' => ['audio/tta', 'audio/x-tta'], + 'ttc' => ['font/collection'], + 'ttf' => ['application/x-font-truetype', 'application/x-font-ttf', 'font/ttf'], + 'ttl' => ['text/turtle'], + 'ttx' => ['application/x-font-ttx'], + 'twd' => ['application/vnd.simtech-mindmapper'], + 'twds' => ['application/vnd.simtech-mindmapper'], + 'twig' => ['text/x-twig'], + 'txd' => ['application/vnd.genomatix.tuxedo'], + 'txf' => ['application/vnd.mobius.txf'], + 'txt' => ['text/plain'], + 'txz' => ['application/x-xz-compressed-tar'], + 'tzo' => ['application/x-tzo'], + 'u32' => ['application/x-authorware-bin'], + 'udeb' => ['application/vnd.debian.binary-package', 'application/x-deb', 'application/x-debian-package'], + 'ufd' => ['application/vnd.ufdl'], + 'ufdl' => ['application/vnd.ufdl'], + 'ufraw' => ['application/x-ufraw'], + 'ui' => ['application/x-designer', 'application/x-gtk-builder'], + 'uil' => ['text/x-uil'], + 'ult' => ['audio/x-mod'], + 'ulx' => ['application/x-glulx'], + 'umj' => ['application/vnd.umajin'], + 'unf' => ['application/x-nes-rom'], + 'uni' => ['audio/x-mod'], + 'unif' => ['application/x-nes-rom'], + 'unityweb' => ['application/vnd.unity'], + 'uoml' => ['application/vnd.uoml+xml'], + 'uri' => ['text/uri-list'], + 'uris' => ['text/uri-list'], + 'url' => ['application/x-mswinurl'], + 'urls' => ['text/uri-list'], + 'ustar' => ['application/x-ustar'], + 'utz' => ['application/vnd.uiq.theme'], + 'uu' => ['text/x-uuencode'], + 'uue' => ['text/x-uuencode', 'zz-application/zz-winassoc-uu'], + 'uva' => ['audio/vnd.dece.audio'], + 'uvd' => ['application/vnd.dece.data'], + 'uvf' => ['application/vnd.dece.data'], + 'uvg' => ['image/vnd.dece.graphic'], + 'uvh' => ['video/vnd.dece.hd'], + 'uvi' => ['image/vnd.dece.graphic'], + 'uvm' => ['video/vnd.dece.mobile'], + 'uvp' => ['video/vnd.dece.pd'], + 'uvs' => ['video/vnd.dece.sd'], + 'uvt' => ['application/vnd.dece.ttml+xml'], + 'uvu' => ['video/vnd.uvvu.mp4'], + 'uvv' => ['video/vnd.dece.video'], + 'uvva' => ['audio/vnd.dece.audio'], + 'uvvd' => ['application/vnd.dece.data'], + 'uvvf' => ['application/vnd.dece.data'], + 'uvvg' => ['image/vnd.dece.graphic'], + 'uvvh' => ['video/vnd.dece.hd'], + 'uvvi' => ['image/vnd.dece.graphic'], + 'uvvm' => ['video/vnd.dece.mobile'], + 'uvvp' => ['video/vnd.dece.pd'], + 'uvvs' => ['video/vnd.dece.sd'], + 'uvvt' => ['application/vnd.dece.ttml+xml'], + 'uvvu' => ['video/vnd.uvvu.mp4'], + 'uvvv' => ['video/vnd.dece.video'], + 'uvvx' => ['application/vnd.dece.unspecified'], + 'uvvz' => ['application/vnd.dece.zip'], + 'uvx' => ['application/vnd.dece.unspecified'], + 'uvz' => ['application/vnd.dece.zip'], + 'v' => ['text/x-verilog'], + 'v64' => ['application/x-n64-rom'], + 'vala' => ['text/x-vala'], + 'vapi' => ['text/x-vala'], + 'vb' => ['application/x-virtual-boy-rom'], + 'vcard' => ['text/directory', 'text/vcard', 'text/x-vcard'], + 'vcd' => ['application/x-cdlink'], + 'vcf' => ['text/x-vcard', 'text/directory', 'text/vcard'], + 'vcg' => ['application/vnd.groove-vcard'], + 'vcs' => ['application/ics', 'text/calendar', 'text/x-vcalendar'], + 'vct' => ['text/directory', 'text/vcard', 'text/x-vcard'], + 'vcx' => ['application/vnd.vcx'], + 'vda' => ['image/x-icb', 'image/x-tga'], + 'vhd' => ['text/x-vhdl'], + 'vhdl' => ['text/x-vhdl'], + 'vis' => ['application/vnd.visionary'], + 'viv' => ['video/vivo', 'video/vnd.vivo'], + 'vivo' => ['video/vivo', 'video/vnd.vivo'], + 'vlc' => ['application/m3u', 'audio/m3u', 'audio/mpegurl', 'audio/x-m3u', 'audio/x-mp3-playlist', 'audio/x-mpegurl'], + 'vob' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2', 'video/x-ms-vob'], + 'voc' => ['audio/x-voc'], + 'vor' => ['application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global'], + 'vox' => ['application/x-authorware-bin'], + 'vrm' => ['model/vrml'], + 'vrml' => ['model/vrml'], + 'vsd' => ['application/vnd.visio'], + 'vsdm' => ['application/vnd.ms-visio.drawing.macroenabled.main+xml'], + 'vsdx' => ['application/vnd.ms-visio.drawing.main+xml'], + 'vsf' => ['application/vnd.vsf'], + 'vss' => ['application/vnd.visio'], + 'vssm' => ['application/vnd.ms-visio.stencil.macroenabled.main+xml'], + 'vssx' => ['application/vnd.ms-visio.stencil.main+xml'], + 'vst' => ['application/vnd.visio', 'image/x-icb', 'image/x-tga'], + 'vstm' => ['application/vnd.ms-visio.template.macroenabled.main+xml'], + 'vstx' => ['application/vnd.ms-visio.template.main+xml'], + 'vsw' => ['application/vnd.visio'], + 'vtt' => ['text/vtt'], + 'vtu' => ['model/vnd.vtu'], + 'vxml' => ['application/voicexml+xml'], + 'w3d' => ['application/x-director'], + 'wad' => ['application/x-doom', 'application/x-doom-wad', 'application/x-wii-wad'], + 'wav' => ['audio/wav', 'audio/vnd.wave', 'audio/x-wav'], + 'wax' => ['application/x-ms-asx', 'audio/x-ms-asx', 'audio/x-ms-wax', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'], + 'wb1' => ['application/x-quattropro'], + 'wb2' => ['application/x-quattropro'], + 'wb3' => ['application/x-quattropro'], + 'wbmp' => ['image/vnd.wap.wbmp'], + 'wbs' => ['application/vnd.criticaltools.wbs+xml'], + 'wbxml' => ['application/vnd.wap.wbxml'], + 'wcm' => ['application/vnd.ms-works'], + 'wdb' => ['application/vnd.ms-works'], + 'wdp' => ['image/vnd.ms-photo'], + 'weba' => ['audio/webm'], + 'webm' => ['video/webm'], + 'webp' => ['image/webp'], + 'wg' => ['application/vnd.pmi.widget'], + 'wgt' => ['application/widget'], + 'wim' => ['application/x-ms-wim'], + 'wk1' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'], + 'wk3' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'], + 'wk4' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'], + 'wkdownload' => ['application/x-partial-download'], + 'wks' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/vnd.ms-works', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'], + 'wm' => ['video/x-ms-wm'], + 'wma' => ['audio/x-ms-wma', 'audio/wma'], + 'wmd' => ['application/x-ms-wmd'], + 'wmf' => ['application/wmf', 'application/x-msmetafile', 'application/x-wmf', 'image/wmf', 'image/x-win-metafile', 'image/x-wmf'], + 'wml' => ['text/vnd.wap.wml'], + 'wmlc' => ['application/vnd.wap.wmlc'], + 'wmls' => ['text/vnd.wap.wmlscript'], + 'wmlsc' => ['application/vnd.wap.wmlscriptc'], + 'wmv' => ['audio/x-ms-wmv', 'video/x-ms-wmv'], + 'wmx' => ['application/x-ms-asx', 'audio/x-ms-asx', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'], + 'wmz' => ['application/x-ms-wmz', 'application/x-msmetafile'], + 'woff' => ['application/font-woff', 'application/x-font-woff', 'font/woff'], + 'woff2' => ['font/woff', 'font/woff2'], + 'wp' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'], + 'wp4' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'], + 'wp5' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'], + 'wp6' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'], + 'wpd' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'], + 'wpg' => ['application/x-wpg'], + 'wpl' => ['application/vnd.ms-wpl'], + 'wpp' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'], + 'wps' => ['application/vnd.ms-works'], + 'wqd' => ['application/vnd.wqd'], + 'wri' => ['application/x-mswrite'], + 'wrl' => ['model/vrml'], + 'ws' => ['application/x-wonderswan-rom'], + 'wsc' => ['application/x-wonderswan-color-rom'], + 'wsdl' => ['application/wsdl+xml'], + 'wsgi' => ['text/x-python'], + 'wspolicy' => ['application/wspolicy+xml'], + 'wtb' => ['application/vnd.webturbo'], + 'wv' => ['audio/x-wavpack'], + 'wvc' => ['audio/x-wavpack-correction'], + 'wvp' => ['audio/x-wavpack'], + 'wvx' => ['application/x-ms-asx', 'audio/x-ms-asx', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'], + 'wwf' => ['application/wwf', 'application/x-wwf'], + 'x32' => ['application/x-authorware-bin'], + 'x3d' => ['model/x3d+xml'], + 'x3db' => ['model/x3d+binary'], + 'x3dbz' => ['model/x3d+binary'], + 'x3dv' => ['model/x3d+vrml'], + 'x3dvz' => ['model/x3d+vrml'], + 'x3dz' => ['model/x3d+xml'], + 'x3f' => ['image/x-sigma-x3f'], + 'xac' => ['application/x-gnucash'], + 'xaml' => ['application/xaml+xml'], + 'xap' => ['application/x-silverlight-app'], + 'xar' => ['application/vnd.xara', 'application/x-xar'], + 'xbap' => ['application/x-ms-xbap'], + 'xbd' => ['application/vnd.fujixerox.docuworks.binder'], + 'xbel' => ['application/x-xbel'], + 'xbl' => ['application/xml', 'text/xml'], + 'xbm' => ['image/x-xbitmap'], + 'xcf' => ['image/x-xcf'], + 'xcf.bz2' => ['image/x-compressed-xcf'], + 'xcf.gz' => ['image/x-compressed-xcf'], + 'xdf' => ['application/xcap-diff+xml'], + 'xdgapp' => ['application/vnd.flatpak', 'application/vnd.xdgapp'], + 'xdm' => ['application/vnd.syncml.dm+xml'], + 'xdp' => ['application/vnd.adobe.xdp+xml'], + 'xdssc' => ['application/dssc+xml'], + 'xdw' => ['application/vnd.fujixerox.docuworks'], + 'xenc' => ['application/xenc+xml'], + 'xer' => ['application/patch-ops-error+xml'], + 'xfdf' => ['application/vnd.adobe.xfdf'], + 'xfdl' => ['application/vnd.xfdl'], + 'xhe' => ['audio/usac'], + 'xht' => ['application/xhtml+xml'], + 'xhtml' => ['application/xhtml+xml'], + 'xhvml' => ['application/xv+xml'], + 'xi' => ['audio/x-xi'], + 'xif' => ['image/vnd.xiff'], + 'xla' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xlam' => ['application/vnd.ms-excel.addin.macroenabled.12'], + 'xlc' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xld' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xlf' => ['application/x-xliff', 'application/x-xliff+xml', 'application/xliff+xml'], + 'xliff' => ['application/x-xliff', 'application/xliff+xml'], + 'xll' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xlm' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xlr' => ['application/vnd.ms-works'], + 'xls' => ['application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xlsb' => ['application/vnd.ms-excel.sheet.binary.macroenabled.12'], + 'xlsm' => ['application/vnd.ms-excel.sheet.macroenabled.12'], + 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], + 'xlt' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xltm' => ['application/vnd.ms-excel.template.macroenabled.12'], + 'xltx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.template'], + 'xlw' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'], + 'xm' => ['audio/x-xm', 'audio/xm'], + 'xmf' => ['audio/mobile-xmf', 'audio/x-xmf', 'audio/xmf'], + 'xmi' => ['text/x-xmi'], + 'xml' => ['application/xml', 'text/xml'], + 'xo' => ['application/vnd.olpc-sugar'], + 'xop' => ['application/xop+xml'], + 'xpi' => ['application/x-xpinstall'], + 'xpl' => ['application/xproc+xml'], + 'xpm' => ['image/x-xpixmap', 'image/x-xpm'], + 'xpr' => ['application/vnd.is-xpr'], + 'xps' => ['application/oxps', 'application/vnd.ms-xpsdocument', 'application/xps'], + 'xpw' => ['application/vnd.intercon.formnet'], + 'xpx' => ['application/vnd.intercon.formnet'], + 'xsd' => ['application/xml', 'text/xml'], + 'xsl' => ['application/xml', 'application/xslt+xml'], + 'xslfo' => ['text/x-xslfo'], + 'xslt' => ['application/xslt+xml'], + 'xsm' => ['application/vnd.syncml+xml'], + 'xspf' => ['application/x-xspf+xml', 'application/xspf+xml'], + 'xul' => ['application/vnd.mozilla.xul+xml'], + 'xvm' => ['application/xv+xml'], + 'xvml' => ['application/xv+xml'], + 'xwd' => ['image/x-xwindowdump'], + 'xyz' => ['chemical/x-xyz'], + 'xz' => ['application/x-xz'], + 'yaml' => ['application/x-yaml', 'text/x-yaml', 'text/yaml'], + 'yang' => ['application/yang'], + 'yin' => ['application/yin+xml'], + 'yml' => ['application/x-yaml', 'text/x-yaml', 'text/yaml'], + 'yt' => ['application/vnd.youtube.yt'], + 'z1' => ['application/x-zmachine'], + 'z2' => ['application/x-zmachine'], + 'z3' => ['application/x-zmachine'], + 'z4' => ['application/x-zmachine'], + 'z5' => ['application/x-zmachine'], + 'z6' => ['application/x-zmachine'], + 'z64' => ['application/x-n64-rom'], + 'z7' => ['application/x-zmachine'], + 'z8' => ['application/x-zmachine'], + 'zabw' => ['application/x-abiword'], + 'zaz' => ['application/vnd.zzazz.deck+xml'], + 'zip' => ['application/zip', 'application/x-zip', 'application/x-zip-compressed'], + 'zir' => ['application/vnd.zul'], + 'zirz' => ['application/vnd.zul'], + 'zmm' => ['application/vnd.handheld-entertainment+xml'], + 'zoo' => ['application/x-zoo'], + 'zsav' => ['application/x-spss-sav', 'application/x-spss-savefile'], + 'zz' => ['application/zlib'], + '123' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'], + '602' => ['application/x-t602'], + '669' => ['audio/x-mod'], + ]; +} diff --git a/vendor/symfony/mime/MimeTypesInterface.php b/vendor/symfony/mime/MimeTypesInterface.php new file mode 100644 index 0000000..9fbd2cc --- /dev/null +++ b/vendor/symfony/mime/MimeTypesInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +/** + * @author Fabien Potencier + */ +interface MimeTypesInterface extends MimeTypeGuesserInterface +{ + /** + * Gets the extensions for the given MIME type. + * + * @return string[] an array of extensions (first one is the preferred one) + */ + public function getExtensions(string $mimeType): array; + + /** + * Gets the MIME types for the given extension. + * + * @return string[] an array of MIME types (first one is the preferred one) + */ + public function getMimeTypes(string $ext): array; +} diff --git a/vendor/symfony/mime/Part/AbstractMultipartPart.php b/vendor/symfony/mime/Part/AbstractMultipartPart.php new file mode 100644 index 0000000..48b8620 --- /dev/null +++ b/vendor/symfony/mime/Part/AbstractMultipartPart.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part; + +use Symfony\Component\Mime\Header\Headers; + +/** + * @author Fabien Potencier + */ +abstract class AbstractMultipartPart extends AbstractPart +{ + private $boundary; + private $parts = []; + + public function __construct(AbstractPart ...$parts) + { + parent::__construct(); + + foreach ($parts as $part) { + $this->parts[] = $part; + } + } + + /** + * @return AbstractPart[] + */ + public function getParts(): array + { + return $this->parts; + } + + public function getMediaType(): string + { + return 'multipart'; + } + + public function getPreparedHeaders(): Headers + { + $headers = parent::getPreparedHeaders(); + $headers->setHeaderParameter('Content-Type', 'boundary', $this->getBoundary()); + + return $headers; + } + + public function bodyToString(): string + { + $parts = $this->getParts(); + $string = ''; + foreach ($parts as $part) { + $string .= '--'.$this->getBoundary()."\r\n".$part->toString()."\r\n"; + } + $string .= '--'.$this->getBoundary()."--\r\n"; + + return $string; + } + + public function bodyToIterable(): iterable + { + $parts = $this->getParts(); + foreach ($parts as $part) { + yield '--'.$this->getBoundary()."\r\n"; + yield from $part->toIterable(); + yield "\r\n"; + } + yield '--'.$this->getBoundary()."--\r\n"; + } + + public function asDebugString(): string + { + $str = parent::asDebugString(); + foreach ($this->getParts() as $part) { + $lines = explode("\n", $part->asDebugString()); + $str .= "\n └ ".array_shift($lines); + foreach ($lines as $line) { + $str .= "\n |".$line; + } + } + + return $str; + } + + private function getBoundary(): string + { + if (null === $this->boundary) { + $this->boundary = '_=_symfony_'.time().'_'.bin2hex(random_bytes(16)).'_=_'; + } + + return $this->boundary; + } +} diff --git a/vendor/symfony/mime/Part/AbstractPart.php b/vendor/symfony/mime/Part/AbstractPart.php new file mode 100644 index 0000000..93892d9 --- /dev/null +++ b/vendor/symfony/mime/Part/AbstractPart.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part; + +use Symfony\Component\Mime\Header\Headers; + +/** + * @author Fabien Potencier + */ +abstract class AbstractPart +{ + private $headers; + + public function __construct() + { + $this->headers = new Headers(); + } + + public function getHeaders(): Headers + { + return $this->headers; + } + + public function getPreparedHeaders(): Headers + { + $headers = clone $this->headers; + $headers->setHeaderBody('Parameterized', 'Content-Type', $this->getMediaType().'/'.$this->getMediaSubtype()); + + return $headers; + } + + public function toString(): string + { + return $this->getPreparedHeaders()->toString()."\r\n".$this->bodyToString(); + } + + public function toIterable(): iterable + { + yield $this->getPreparedHeaders()->toString(); + yield "\r\n"; + yield from $this->bodyToIterable(); + } + + public function asDebugString(): string + { + return $this->getMediaType().'/'.$this->getMediaSubtype(); + } + + abstract public function bodyToString(): string; + + abstract public function bodyToIterable(): iterable; + + abstract public function getMediaType(): string; + + abstract public function getMediaSubtype(): string; +} diff --git a/vendor/symfony/mime/Part/DataPart.php b/vendor/symfony/mime/Part/DataPart.php new file mode 100644 index 0000000..423185f --- /dev/null +++ b/vendor/symfony/mime/Part/DataPart.php @@ -0,0 +1,161 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part; + +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Header\Headers; +use Symfony\Component\Mime\MimeTypes; + +/** + * @author Fabien Potencier + */ +class DataPart extends TextPart +{ + private static $mimeTypes; + + private $filename; + private $mediaType; + private $cid; + private $handle; + + /** + * @param resource|string $body + */ + public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null) + { + if (null === $contentType) { + $contentType = 'application/octet-stream'; + } + list($this->mediaType, $subtype) = explode('/', $contentType); + + parent::__construct($body, null, $subtype, $encoding); + + $this->filename = $filename; + $this->setName($filename); + $this->setDisposition('attachment'); + } + + public static function fromPath(string $path, string $name = null, string $contentType = null): self + { + // FIXME: if file is not readable, exception? + + if (null === $contentType) { + $ext = strtolower(substr($path, strrpos($path, '.') + 1)); + if (null === self::$mimeTypes) { + self::$mimeTypes = new MimeTypes(); + } + $contentType = self::$mimeTypes->getMimeTypes($ext)[0] ?? 'application/octet-stream'; + } + + if (false === $handle = @fopen($path, 'r', false)) { + throw new InvalidArgumentException(sprintf('Unable to open path "%s"', $path)); + } + $p = new self($handle, $name ?: basename($path), $contentType); + $p->handle = $handle; + + return $p; + } + + /** + * @return $this + */ + public function asInline() + { + return $this->setDisposition('inline'); + } + + public function getContentId(): string + { + return $this->cid ?: $this->cid = $this->generateContentId(); + } + + public function hasContentId(): bool + { + return null !== $this->cid; + } + + public function getMediaType(): string + { + return $this->mediaType; + } + + public function getPreparedHeaders(): Headers + { + $headers = parent::getPreparedHeaders(); + + if (null !== $this->cid) { + $headers->setHeaderBody('Id', 'Content-ID', $this->cid); + } + + if (null !== $this->filename) { + $headers->setHeaderParameter('Content-Disposition', 'filename', $this->filename); + } + + return $headers; + } + + public function asDebugString(): string + { + $str = parent::asDebugString(); + if (null !== $this->filename) { + $str .= ' filename: '.$this->filename; + } + + return $str; + } + + private function generateContentId(): string + { + return bin2hex(random_bytes(16)).'@symfony'; + } + + public function __destruct() + { + if (null !== $this->handle && \is_resource($this->handle)) { + fclose($this->handle); + } + } + + /** + * @return array + */ + public function __sleep() + { + // converts the body to a string + parent::__sleep(); + + $this->_parent = []; + foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) { + $r = new \ReflectionProperty(TextPart::class, $name); + $r->setAccessible(true); + $this->_parent[$name] = $r->getValue($this); + } + $this->_headers = $this->getHeaders(); + + return ['_headers', '_parent', 'filename', 'mediaType']; + } + + public function __wakeup() + { + $r = new \ReflectionProperty(AbstractPart::class, 'headers'); + $r->setAccessible(true); + $r->setValue($this, $this->_headers); + unset($this->_headers); + + foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) { + $r = new \ReflectionProperty(TextPart::class, $name); + $r->setAccessible(true); + $r->setValue($this, $this->_parent[$name]); + } + unset($this->_parent); + } +} diff --git a/vendor/symfony/mime/Part/MessagePart.php b/vendor/symfony/mime/Part/MessagePart.php new file mode 100644 index 0000000..1b5c23e --- /dev/null +++ b/vendor/symfony/mime/Part/MessagePart.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part; + +use Symfony\Component\Mime\Message; +use Symfony\Component\Mime\RawMessage; + +/** + * @final + * + * @author Fabien Potencier + */ +class MessagePart extends DataPart +{ + private $message; + + public function __construct(RawMessage $message) + { + if ($message instanceof Message) { + $name = $message->getHeaders()->getHeaderBody('Subject').'.eml'; + } else { + $name = 'email.eml'; + } + parent::__construct('', $name); + + $this->message = $message; + } + + public function getMediaType(): string + { + return 'message'; + } + + public function getMediaSubtype(): string + { + return 'rfc822'; + } + + public function getBody(): string + { + return $this->message->toString(); + } + + public function bodyToString(): string + { + return $this->getBody(); + } + + public function bodyToIterable(): iterable + { + return $this->message->toIterable(); + } +} diff --git a/vendor/symfony/mime/Part/Multipart/AlternativePart.php b/vendor/symfony/mime/Part/Multipart/AlternativePart.php new file mode 100644 index 0000000..fd75423 --- /dev/null +++ b/vendor/symfony/mime/Part/Multipart/AlternativePart.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part\Multipart; + +use Symfony\Component\Mime\Part\AbstractMultipartPart; + +/** + * @author Fabien Potencier + */ +final class AlternativePart extends AbstractMultipartPart +{ + public function getMediaSubtype(): string + { + return 'alternative'; + } +} diff --git a/vendor/symfony/mime/Part/Multipart/DigestPart.php b/vendor/symfony/mime/Part/Multipart/DigestPart.php new file mode 100644 index 0000000..27537f1 --- /dev/null +++ b/vendor/symfony/mime/Part/Multipart/DigestPart.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part\Multipart; + +use Symfony\Component\Mime\Part\AbstractMultipartPart; +use Symfony\Component\Mime\Part\MessagePart; + +/** + * @author Fabien Potencier + */ +final class DigestPart extends AbstractMultipartPart +{ + public function __construct(MessagePart ...$parts) + { + parent::__construct(...$parts); + } + + public function getMediaSubtype(): string + { + return 'digest'; + } +} diff --git a/vendor/symfony/mime/Part/Multipart/FormDataPart.php b/vendor/symfony/mime/Part/Multipart/FormDataPart.php new file mode 100644 index 0000000..6838620 --- /dev/null +++ b/vendor/symfony/mime/Part/Multipart/FormDataPart.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part\Multipart; + +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Part\AbstractMultipartPart; +use Symfony\Component\Mime\Part\DataPart; +use Symfony\Component\Mime\Part\TextPart; + +/** + * Implements RFC 7578. + * + * @author Fabien Potencier + */ +final class FormDataPart extends AbstractMultipartPart +{ + private $fields = []; + + /** + * @param (string|array|DataPart)[] $fields + */ + public function __construct(array $fields = []) + { + parent::__construct(); + + foreach ($fields as $name => $value) { + if (!\is_string($value) && !\is_array($value) && !$value instanceof TextPart) { + throw new InvalidArgumentException(sprintf('A form field value can only be a string, an array, or an instance of TextPart ("%s" given).', \is_object($value) ? \get_class($value) : \gettype($value))); + } + + $this->fields[$name] = $value; + } + // HTTP does not support \r\n in header values + $this->getHeaders()->setMaxLineLength(PHP_INT_MAX); + } + + public function getMediaSubtype(): string + { + return 'form-data'; + } + + public function getParts(): array + { + return $this->prepareFields($this->fields); + } + + private function prepareFields(array $fields): array + { + $values = []; + + $prepare = function ($item, $key, $root = null) use (&$values, &$prepare) { + $fieldName = $root ? sprintf('%s[%s]', $root, $key) : $key; + + if (\is_array($item)) { + array_walk($item, $prepare, $fieldName); + + return; + } + + $values[] = $this->preparePart($fieldName, $item); + }; + + array_walk($fields, $prepare); + + return $values; + } + + private function preparePart(string $name, $value): TextPart + { + if (\is_string($value)) { + return $this->configurePart($name, new TextPart($value, 'utf-8', 'plain', '8bit')); + } + + return $this->configurePart($name, $value); + } + + private function configurePart(string $name, TextPart $part): TextPart + { + static $r; + + if (null === $r) { + $r = new \ReflectionProperty(TextPart::class, 'encoding'); + $r->setAccessible(true); + } + + $part->setDisposition('form-data'); + $part->setName($name); + // HTTP does not support \r\n in header values + $part->getHeaders()->setMaxLineLength(PHP_INT_MAX); + $r->setValue($part, '8bit'); + + return $part; + } +} diff --git a/vendor/symfony/mime/Part/Multipart/MixedPart.php b/vendor/symfony/mime/Part/Multipart/MixedPart.php new file mode 100644 index 0000000..c8d7028 --- /dev/null +++ b/vendor/symfony/mime/Part/Multipart/MixedPart.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part\Multipart; + +use Symfony\Component\Mime\Part\AbstractMultipartPart; + +/** + * @author Fabien Potencier + */ +final class MixedPart extends AbstractMultipartPart +{ + public function getMediaSubtype(): string + { + return 'mixed'; + } +} diff --git a/vendor/symfony/mime/Part/Multipart/RelatedPart.php b/vendor/symfony/mime/Part/Multipart/RelatedPart.php new file mode 100644 index 0000000..08fdd5f --- /dev/null +++ b/vendor/symfony/mime/Part/Multipart/RelatedPart.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part\Multipart; + +use Symfony\Component\Mime\Part\AbstractMultipartPart; +use Symfony\Component\Mime\Part\AbstractPart; + +/** + * @author Fabien Potencier + */ +final class RelatedPart extends AbstractMultipartPart +{ + private $mainPart; + + public function __construct(AbstractPart $mainPart, AbstractPart $part, AbstractPart ...$parts) + { + $this->mainPart = $mainPart; + $this->prepareParts($part, ...$parts); + + parent::__construct($part, ...$parts); + } + + public function getParts(): array + { + return array_merge([$this->mainPart], parent::getParts()); + } + + public function getMediaSubtype(): string + { + return 'related'; + } + + private function generateContentId(): string + { + return bin2hex(random_bytes(16)).'@symfony'; + } + + private function prepareParts(AbstractPart ...$parts): void + { + foreach ($parts as $part) { + if (!$part->getHeaders()->has('Content-ID')) { + $part->getHeaders()->setHeaderBody('Id', 'Content-ID', $this->generateContentId()); + } + } + } +} diff --git a/vendor/symfony/mime/Part/SMimePart.php b/vendor/symfony/mime/Part/SMimePart.php new file mode 100644 index 0000000..1dfc1ae --- /dev/null +++ b/vendor/symfony/mime/Part/SMimePart.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part; + +use Symfony\Component\Mime\Header\Headers; + +/** + * @author Sebastiaan Stok + */ +class SMimePart extends AbstractPart +{ + private $body; + private $type; + private $subtype; + private $parameters; + + /** + * @param iterable|string $body + */ + public function __construct($body, string $type, string $subtype, array $parameters) + { + parent::__construct(); + + if (!\is_string($body) && !is_iterable($body)) { + throw new \TypeError(sprintf('The body of "%s" must be a string or a iterable (got "%s").', self::class, \is_object($body) ? \get_class($body) : \gettype($body))); + } + + $this->body = $body; + $this->type = $type; + $this->subtype = $subtype; + $this->parameters = $parameters; + } + + public function getMediaType(): string + { + return $this->type; + } + + public function getMediaSubtype(): string + { + return $this->subtype; + } + + public function bodyToString(): string + { + if (\is_string($this->body)) { + return $this->body; + } + + $body = ''; + foreach ($this->body as $chunk) { + $body .= $chunk; + } + $this->body = $body; + + return $body; + } + + public function bodyToIterable(): iterable + { + if (\is_string($this->body)) { + yield $this->body; + + return; + } + + $body = ''; + foreach ($this->body as $chunk) { + $body .= $chunk; + yield $chunk; + } + $this->body = $body; + } + + public function getPreparedHeaders(): Headers + { + $headers = clone parent::getHeaders(); + + $headers->setHeaderBody('Parameterized', 'Content-Type', $this->getMediaType().'/'.$this->getMediaSubtype()); + + foreach ($this->parameters as $name => $value) { + $headers->setHeaderParameter('Content-Type', $name, $value); + } + + return $headers; + } + + public function __sleep(): array + { + // convert iterables to strings for serialization + if (is_iterable($this->body)) { + $this->body = $this->bodyToString(); + } + + $this->_headers = $this->getHeaders(); + + return ['_headers', 'body', 'type', 'subtype', 'parameters']; + } + + public function __wakeup(): void + { + $r = new \ReflectionProperty(AbstractPart::class, 'headers'); + $r->setAccessible(true); + $r->setValue($this, $this->_headers); + unset($this->_headers); + } +} diff --git a/vendor/symfony/mime/Part/TextPart.php b/vendor/symfony/mime/Part/TextPart.php new file mode 100644 index 0000000..a41d91d --- /dev/null +++ b/vendor/symfony/mime/Part/TextPart.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Part; + +use Symfony\Component\Mime\Encoder\Base64ContentEncoder; +use Symfony\Component\Mime\Encoder\ContentEncoderInterface; +use Symfony\Component\Mime\Encoder\EightBitContentEncoder; +use Symfony\Component\Mime\Encoder\QpContentEncoder; +use Symfony\Component\Mime\Exception\InvalidArgumentException; +use Symfony\Component\Mime\Header\Headers; + +/** + * @author Fabien Potencier + */ +class TextPart extends AbstractPart +{ + private static $encoders = []; + + private $body; + private $charset; + private $subtype; + private $disposition; + private $name; + private $encoding; + + /** + * @param resource|string $body + */ + public function __construct($body, ?string $charset = 'utf-8', $subtype = 'plain', string $encoding = null) + { + parent::__construct(); + + if (!\is_string($body) && !\is_resource($body)) { + throw new \TypeError(sprintf('The body of "%s" must be a string or a resource (got "%s").', self::class, \is_object($body) ? \get_class($body) : \gettype($body))); + } + + $this->body = $body; + $this->charset = $charset; + $this->subtype = $subtype; + + if (null === $encoding) { + $this->encoding = $this->chooseEncoding(); + } else { + if ('quoted-printable' !== $encoding && 'base64' !== $encoding && '8bit' !== $encoding) { + throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable", "base64", or "8bit" ("%s" given).', $encoding)); + } + $this->encoding = $encoding; + } + } + + public function getMediaType(): string + { + return 'text'; + } + + public function getMediaSubtype(): string + { + return $this->subtype; + } + + /** + * @param string $disposition one of attachment, inline, or form-data + * + * @return $this + */ + public function setDisposition(string $disposition) + { + $this->disposition = $disposition; + + return $this; + } + + /** + * Sets the name of the file (used by FormDataPart). + * + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getBody(): string + { + if (!\is_resource($this->body)) { + return $this->body; + } + + if (stream_get_meta_data($this->body)['seekable'] ?? false) { + rewind($this->body); + } + + return stream_get_contents($this->body) ?: ''; + } + + public function bodyToString(): string + { + return $this->getEncoder()->encodeString($this->getBody(), $this->charset); + } + + public function bodyToIterable(): iterable + { + if (\is_resource($this->body)) { + if (stream_get_meta_data($this->body)['seekable'] ?? false) { + rewind($this->body); + } + yield from $this->getEncoder()->encodeByteStream($this->body); + } else { + yield $this->getEncoder()->encodeString($this->body); + } + } + + public function getPreparedHeaders(): Headers + { + $headers = parent::getPreparedHeaders(); + + $headers->setHeaderBody('Parameterized', 'Content-Type', $this->getMediaType().'/'.$this->getMediaSubtype()); + if ($this->charset) { + $headers->setHeaderParameter('Content-Type', 'charset', $this->charset); + } + if ($this->name) { + $headers->setHeaderParameter('Content-Type', 'name', $this->name); + } + $headers->setHeaderBody('Text', 'Content-Transfer-Encoding', $this->encoding); + + if (!$headers->has('Content-Disposition') && null !== $this->disposition) { + $headers->setHeaderBody('Parameterized', 'Content-Disposition', $this->disposition); + if ($this->name) { + $headers->setHeaderParameter('Content-Disposition', 'name', $this->name); + } + } + + return $headers; + } + + public function asDebugString(): string + { + $str = parent::asDebugString(); + if (null !== $this->charset) { + $str .= ' charset: '.$this->charset; + } + if (null !== $this->disposition) { + $str .= ' disposition: '.$this->disposition; + } + + return $str; + } + + private function getEncoder(): ContentEncoderInterface + { + if ('8bit' === $this->encoding) { + return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new EightBitContentEncoder()); + } + + if ('quoted-printable' === $this->encoding) { + return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new QpContentEncoder()); + } + + return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new Base64ContentEncoder()); + } + + private function chooseEncoding(): string + { + if (null === $this->charset) { + return 'base64'; + } + + return 'quoted-printable'; + } + + /** + * @return array + */ + public function __sleep() + { + // convert resources to strings for serialization + if (\is_resource($this->body)) { + $this->body = $this->getBody(); + } + + $this->_headers = $this->getHeaders(); + + return ['_headers', 'body', 'charset', 'subtype', 'disposition', 'name', 'encoding']; + } + + public function __wakeup() + { + $r = new \ReflectionProperty(AbstractPart::class, 'headers'); + $r->setAccessible(true); + $r->setValue($this, $this->_headers); + unset($this->_headers); + } +} diff --git a/vendor/symfony/mime/README.md b/vendor/symfony/mime/README.md new file mode 100644 index 0000000..4d565c9 --- /dev/null +++ b/vendor/symfony/mime/README.md @@ -0,0 +1,13 @@ +MIME Component +============== + +The MIME component allows manipulating MIME messages. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/mime.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/mime/RawMessage.php b/vendor/symfony/mime/RawMessage.php new file mode 100644 index 0000000..79a27e9 --- /dev/null +++ b/vendor/symfony/mime/RawMessage.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime; + +use Symfony\Component\Mime\Exception\LogicException; + +/** + * @author Fabien Potencier + */ +class RawMessage implements \Serializable +{ + private $message; + + /** + * @param iterable|string $message + */ + public function __construct($message) + { + $this->message = $message; + } + + public function toString(): string + { + if (\is_string($this->message)) { + return $this->message; + } + + return $this->message = implode('', iterator_to_array($this->message, false)); + } + + public function toIterable(): iterable + { + if (\is_string($this->message)) { + yield $this->message; + + return; + } + + $message = ''; + foreach ($this->message as $chunk) { + $message .= $chunk; + yield $chunk; + } + $this->message = $message; + } + + /** + * @throws LogicException if the message is not valid + */ + public function ensureValidity() + { + } + + /** + * @internal + */ + final public function serialize(): string + { + return serialize($this->__serialize()); + } + + /** + * @internal + */ + final public function unserialize($serialized) + { + $this->__unserialize(unserialize($serialized)); + } + + public function __serialize(): array + { + return [$this->message]; + } + + public function __unserialize(array $data): void + { + [$this->message] = $data; + } +} diff --git a/vendor/symfony/mime/Resources/bin/update_mime_types.php b/vendor/symfony/mime/Resources/bin/update_mime_types.php new file mode 100644 index 0000000..74a9449 --- /dev/null +++ b/vendor/symfony/mime/Resources/bin/update_mime_types.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// load new map +$data = file_get_contents('https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types'); +$new = []; +foreach (explode("\n", $data) as $line) { + if (!$line || '#' == $line[0]) { + continue; + } + $mimeType = substr($line, 0, strpos($line, "\t")); + $extensions = explode(' ', substr($line, strrpos($line, "\t") + 1)); + $new[$mimeType] = $extensions; +} + +$xml = simplexml_load_string(file_get_contents('https://raw.github.com/minad/mimemagic/master/script/freedesktop.org.xml')); +foreach ($xml as $node) { + $exts = []; + foreach ($node->glob as $glob) { + $pattern = (string) $glob['pattern']; + if ('*' != $pattern[0] || '.' != $pattern[1]) { + continue; + } + + $exts[] = substr($pattern, 2); + } + + if (!$exts) { + continue; + } + + $mt = strtolower((string) $node['type']); + $new[$mt] = array_merge($new[$mt] ?? [], $exts); + foreach ($node->alias as $alias) { + $mt = strtolower((string) $alias['type']); + $new[$mt] = array_merge($new[$mt] ?? [], $exts); + } +} + +// load current map +$data = file_get_contents($output = __DIR__.'/../../MimeTypes.php'); +$current = []; +$pre = ''; +$post = ''; +foreach (explode("\n", $data) as $line) { + if (!preg_match("{^ '([^']+/[^']+)' => \['(.+)'\],$}", $line, $matches)) { + if (!$current) { + $pre .= $line."\n"; + } else { + $post .= $line."\n"; + } + continue; + } + $current[$matches[1]] = explode("', '", $matches[2]); +} + +// we merge the 2 maps (we never remove old mime types) +$map = array_replace_recursive($current, $new); +ksort($map); + +$data = $pre; +foreach ($map as $mimeType => $exts) { + $data .= sprintf(" '%s' => ['%s'],\n", $mimeType, implode("', '", array_unique($exts))); +} +$data .= $post; + +// reverse map +// we prefill the extensions with some preferences for content-types +$exts = [ + 'aif' => ['audio/x-aiff'], + 'aiff' => ['audio/x-aiff'], + 'aps' => ['application/postscript'], + 'avi' => ['video/avi'], + 'bmp' => ['image/bmp'], + 'bz2' => ['application/x-bz2'], + 'css' => ['text/css'], + 'csv' => ['text/csv'], + 'dmg' => ['application/x-apple-diskimage'], + 'doc' => ['application/msword'], + 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], + 'eml' => ['message/rfc822'], + 'exe' => ['application/x-ms-dos-executable'], + 'flv' => ['video/x-flv'], + 'gif' => ['image/gif'], + 'gz' => ['application/x-gzip'], + 'hqx' => ['application/stuffit'], + 'htm' => ['text/html'], + 'html' => ['text/html'], + 'jar' => ['application/x-java-archive'], + 'jpeg' => ['image/jpeg'], + 'jpg' => ['image/jpeg'], + 'js' => ['text/javascript'], + 'm3u' => ['audio/x-mpegurl'], + 'm4a' => ['audio/mp4'], + 'mdb' => ['application/x-msaccess'], + 'mid' => ['audio/midi'], + 'midi' => ['audio/midi'], + 'mov' => ['video/quicktime'], + 'mp3' => ['audio/mpeg'], + 'mp4' => ['video/mp4'], + 'mpeg' => ['video/mpeg'], + 'mpg' => ['video/mpeg'], + 'ogg' => ['audio/ogg'], + 'pdf' => ['application/pdf'], + 'php' => ['application/x-php'], + 'php3' => ['application/x-php'], + 'php4' => ['application/x-php'], + 'php5' => ['application/x-php'], + 'png' => ['image/png'], + 'ppt' => ['application/vnd.ms-powerpoint'], + 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'], + 'ps' => ['application/postscript'], + 'rar' => ['application/x-rar-compressed'], + 'rtf' => ['application/rtf'], + 'sit' => ['application/x-stuffit'], + 'svg' => ['image/svg+xml'], + 'tar' => ['application/x-tar'], + 'tif' => ['image/tiff'], + 'tiff' => ['image/tiff'], + 'ttf' => ['application/x-font-truetype'], + 'txt' => ['text/plain'], + 'vcf' => ['text/x-vcard'], + 'wav' => ['audio/wav'], + 'wma' => ['audio/x-ms-wma'], + 'wmv' => ['audio/x-ms-wmv'], + 'xls' => ['application/vnd.ms-excel'], + 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], + 'xml' => ['application/xml'], + 'zip' => ['application/zip'], +]; +foreach ($map as $mimeType => $extensions) { + foreach ($extensions as $extension) { + $exts[$extension][] = $mimeType; + } +} +ksort($exts); + +$updated = ''; +$state = 0; +foreach (explode("\n", $data) as $line) { + if (!preg_match("{^ '([^'/]+)' => \['(.+)'\],$}", $line, $matches)) { + if (1 === $state) { + $state = 2; + foreach ($exts as $ext => $mimeTypes) { + $updated .= sprintf(" '%s' => ['%s'],\n", $ext, implode("', '", array_unique($mimeTypes))); + } + } + $updated .= $line."\n"; + continue; + } + $state = 1; +} + +$updated = preg_replace('{Updated from upstream on .+?\.}', 'Updated from upstream on '.date('Y-m-d'), $updated, -1); + +file_put_contents($output, rtrim($updated, "\n")."\n"); + +echo "Done.\n"; diff --git a/vendor/symfony/mime/Test/Constraint/EmailAddressContains.php b/vendor/symfony/mime/Test/Constraint/EmailAddressContains.php new file mode 100644 index 0000000..58ef360 --- /dev/null +++ b/vendor/symfony/mime/Test/Constraint/EmailAddressContains.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Mime\Header\MailboxHeader; +use Symfony\Component\Mime\Header\MailboxListHeader; +use Symfony\Component\Mime\RawMessage; + +final class EmailAddressContains extends Constraint +{ + private $headerName; + private $expectedValue; + + public function __construct(string $headerName, string $expectedValue) + { + $this->headerName = $headerName; + $this->expectedValue = $expectedValue; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('contains address "%s" with value "%s"', $this->headerName, $this->expectedValue); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function matches($message): bool + { + if (RawMessage::class === \get_class($message)) { + throw new \LogicException('Unable to test a message address on a RawMessage instance.'); + } + + $header = $message->getHeaders()->get($this->headerName); + if ($header instanceof MailboxHeader) { + return $this->expectedValue === $header->Address()->getAddress(); + } elseif ($header instanceof MailboxListHeader) { + foreach ($header->getAddresses() as $address) { + if ($this->expectedValue === $address->getAddress()) { + return true; + } + } + + return false; + } + + throw new \LogicException(sprintf('Unable to test a message address on a non-address header.')); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function failureDescription($message): string + { + return sprintf('the Email %s (value is %s)', $this->toString(), $message->getHeaders()->get($this->headerName)->getBodyAsString()); + } +} diff --git a/vendor/symfony/mime/Test/Constraint/EmailAttachmentCount.php b/vendor/symfony/mime/Test/Constraint/EmailAttachmentCount.php new file mode 100644 index 0000000..c0adbe3 --- /dev/null +++ b/vendor/symfony/mime/Test/Constraint/EmailAttachmentCount.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Mime\Message; +use Symfony\Component\Mime\RawMessage; + +final class EmailAttachmentCount extends Constraint +{ + private $expectedValue; + private $transport; + + public function __construct(int $expectedValue, string $transport = null) + { + $this->expectedValue = $expectedValue; + $this->transport = $transport; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('has sent "%d" attachment(s)', $this->expectedValue); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function matches($message): bool + { + if (RawMessage::class === \get_class($message) || Message::class === \get_class($message)) { + throw new \LogicException('Unable to test a message attachment on a RawMessage or Message instance.'); + } + + return $this->expectedValue === \count($message->getAttachments()); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function failureDescription($message): string + { + return 'the Email '.$this->toString(); + } +} diff --git a/vendor/symfony/mime/Test/Constraint/EmailHasHeader.php b/vendor/symfony/mime/Test/Constraint/EmailHasHeader.php new file mode 100644 index 0000000..a29f835 --- /dev/null +++ b/vendor/symfony/mime/Test/Constraint/EmailHasHeader.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Mime\RawMessage; + +final class EmailHasHeader extends Constraint +{ + private $headerName; + + public function __construct(string $headerName) + { + $this->headerName = $headerName; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('has header "%s"', $this->headerName); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function matches($message): bool + { + if (RawMessage::class === \get_class($message)) { + throw new \LogicException('Unable to test a message header on a RawMessage instance.'); + } + + return $message->getHeaders()->has($this->headerName); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function failureDescription($message): string + { + return 'the Email '.$this->toString(); + } +} diff --git a/vendor/symfony/mime/Test/Constraint/EmailHeaderSame.php b/vendor/symfony/mime/Test/Constraint/EmailHeaderSame.php new file mode 100644 index 0000000..bc7e330 --- /dev/null +++ b/vendor/symfony/mime/Test/Constraint/EmailHeaderSame.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Mime\RawMessage; + +final class EmailHeaderSame extends Constraint +{ + private $headerName; + private $expectedValue; + + public function __construct(string $headerName, string $expectedValue) + { + $this->headerName = $headerName; + $this->expectedValue = $expectedValue; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('has header "%s" with value "%s"', $this->headerName, $this->expectedValue); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function matches($message): bool + { + if (RawMessage::class === \get_class($message)) { + throw new \LogicException('Unable to test a message header on a RawMessage instance.'); + } + + return $this->expectedValue === $message->getHeaders()->get($this->headerName)->getBodyAsString(); + } + + /** + * @param RawMessage $message + * + * {@inheritdoc} + */ + protected function failureDescription($message): string + { + return sprintf('the Email %s (value is %s)', $this->toString(), $message->getHeaders()->get($this->headerName)->getBodyAsString()); + } +} diff --git a/vendor/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php b/vendor/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php new file mode 100644 index 0000000..3c61376 --- /dev/null +++ b/vendor/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Mime\Message; +use Symfony\Component\Mime\RawMessage; + +final class EmailHtmlBodyContains extends Constraint +{ + private $expectedText; + + public function __construct(string $expectedText) + { + $this->expectedText = $expectedText; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('contains "%s"', $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param RawMessage $message + */ + protected function matches($message): bool + { + if (RawMessage::class === \get_class($message) || Message::class === \get_class($message)) { + throw new \LogicException('Unable to test a message HTML body on a RawMessage or Message instance.'); + } + + return false !== mb_strpos($message->getHtmlBody(), $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param RawMessage $message + */ + protected function failureDescription($message): string + { + return 'the Email HTML body '.$this->toString(); + } +} diff --git a/vendor/symfony/mime/Test/Constraint/EmailTextBodyContains.php b/vendor/symfony/mime/Test/Constraint/EmailTextBodyContains.php new file mode 100644 index 0000000..063d963 --- /dev/null +++ b/vendor/symfony/mime/Test/Constraint/EmailTextBodyContains.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mime\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Mime\Message; +use Symfony\Component\Mime\RawMessage; + +final class EmailTextBodyContains extends Constraint +{ + private $expectedText; + + public function __construct(string $expectedText) + { + $this->expectedText = $expectedText; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('contains "%s"', $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param RawMessage $message + */ + protected function matches($message): bool + { + if (RawMessage::class === \get_class($message) || Message::class === \get_class($message)) { + throw new \LogicException('Unable to test a message text body on a RawMessage or Message instance.'); + } + + return false !== mb_strpos($message->getTextBody(), $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param RawMessage $message + */ + protected function failureDescription($message): string + { + return 'the Email text body '.$this->toString(); + } +} diff --git a/vendor/symfony/mime/composer.json b/vendor/symfony/mime/composer.json new file mode 100644 index 0000000..c3f2626 --- /dev/null +++ b/vendor/symfony/mime/composer.json @@ -0,0 +1,42 @@ +{ + "name": "symfony/mime", + "type": "library", + "description": "A library to manipulate MIME messages", + "keywords": ["mime", "mime-type"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^7.2.5", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10", + "symfony/dependency-injection": "^4.4|^5.0" + }, + "conflict": { + "symfony/mailer": "<4.4" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Mime\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + } +} diff --git a/vendor/symfony/polyfill-intl-idn/Idn.php b/vendor/symfony/polyfill-intl-idn/Idn.php new file mode 100644 index 0000000..adb718d --- /dev/null +++ b/vendor/symfony/polyfill-intl-idn/Idn.php @@ -0,0 +1,283 @@ + + * @author Sebastian Kroczek + * @author Dmitry Lukashin + * @author Laurent Bassin + * + * @internal + */ +final class Idn +{ + const INTL_IDNA_VARIANT_2003 = 0; + const INTL_IDNA_VARIANT_UTS46 = 1; + + private static $encodeTable = array( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + ); + + private static $decodeTable = array( + 'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5, + 'g' => 6, 'h' => 7, 'i' => 8, 'j' => 9, 'k' => 10, 'l' => 11, + 'm' => 12, 'n' => 13, 'o' => 14, 'p' => 15, 'q' => 16, 'r' => 17, + 's' => 18, 't' => 19, 'u' => 20, 'v' => 21, 'w' => 22, 'x' => 23, + 'y' => 24, 'z' => 25, '0' => 26, '1' => 27, '2' => 28, '3' => 29, + '4' => 30, '5' => 31, '6' => 32, '7' => 33, '8' => 34, '9' => 35, + ); + + public static function idn_to_ascii($domain, $options, $variant, &$idna_info = array()) + { + if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) { + @trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED); + } + + if (self::INTL_IDNA_VARIANT_UTS46 === $variant) { + $domain = mb_strtolower($domain, 'utf-8'); + } + + $parts = explode('.', $domain); + + foreach ($parts as $i => &$part) { + if ('' === $part && \count($parts) > 1 + $i) { + return false; + } + if (false === $part = self::encodePart($part)) { + return false; + } + } + + $output = implode('.', $parts); + + $idna_info = array( + 'result' => \strlen($output) > 255 ? false : $output, + 'isTransitionalDifferent' => false, + 'errors' => 0, + ); + + return $idna_info['result']; + } + + public static function idn_to_utf8($domain, $options, $variant, &$idna_info = array()) + { + if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) { + @trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED); + } + + $parts = explode('.', $domain); + + foreach ($parts as &$part) { + $length = \strlen($part); + if ($length < 1 || 63 < $length) { + continue; + } + if (0 !== strpos($part, 'xn--')) { + continue; + } + + $part = substr($part, 4); + $part = self::decodePart($part); + } + + $output = implode('.', $parts); + + $idna_info = array( + 'result' => \strlen($output) > 255 ? false : $output, + 'isTransitionalDifferent' => false, + 'errors' => 0, + ); + + return $idna_info['result']; + } + + private static function encodePart($input) + { + $codePoints = self::listCodePoints($input); + + $n = 128; + $bias = 72; + $delta = 0; + $h = $b = \count($codePoints['basic']); + + $output = ''; + foreach ($codePoints['basic'] as $code) { + $output .= mb_chr($code, 'utf-8'); + } + if ($input === $output) { + return $output; + } + if ($b > 0) { + $output .= '-'; + } + + $codePoints['nonBasic'] = array_unique($codePoints['nonBasic']); + sort($codePoints['nonBasic']); + + $i = 0; + $length = mb_strlen($input, 'utf-8'); + while ($h < $length) { + $m = $codePoints['nonBasic'][$i++]; + $delta += ($m - $n) * ($h + 1); + $n = $m; + + foreach ($codePoints['all'] as $c) { + if ($c < $n || $c < 128) { + ++$delta; + } + if ($c === $n) { + $q = $delta; + for ($k = 36;; $k += 36) { + $t = self::calculateThreshold($k, $bias); + if ($q < $t) { + break; + } + + $code = $t + (($q - $t) % (36 - $t)); + $output .= self::$encodeTable[$code]; + + $q = ($q - $t) / (36 - $t); + } + + $output .= self::$encodeTable[$q]; + $bias = self::adapt($delta, $h + 1, ($h === $b)); + $delta = 0; + ++$h; + } + } + + ++$delta; + ++$n; + } + + $output = 'xn--'.$output; + + return \strlen($output) < 1 || 63 < \strlen($output) ? false : strtolower($output); + } + + private static function listCodePoints($input) + { + $codePoints = array( + 'all' => array(), + 'basic' => array(), + 'nonBasic' => array(), + ); + + $length = mb_strlen($input, 'utf-8'); + for ($i = 0; $i < $length; ++$i) { + $char = mb_substr($input, $i, 1, 'utf-8'); + $code = mb_ord($char, 'utf-8'); + if ($code < 128) { + $codePoints['all'][] = $codePoints['basic'][] = $code; + } else { + $codePoints['all'][] = $codePoints['nonBasic'][] = $code; + } + } + + return $codePoints; + } + + private static function calculateThreshold($k, $bias) + { + if ($k <= $bias + 1) { + return 1; + } + if ($k >= $bias + 26) { + return 26; + } + + return $k - $bias; + } + + private static function adapt($delta, $numPoints, $firstTime) + { + $delta = (int) ($firstTime ? $delta / 700 : $delta / 2); + $delta += (int) ($delta / $numPoints); + + $k = 0; + while ($delta > 35 * 13) { + $delta = (int) ($delta / 35); + $k = $k + 36; + } + + return $k + (int) (36 * $delta / ($delta + 38)); + } + + private static function decodePart($input) + { + $n = 128; + $i = 0; + $bias = 72; + $output = ''; + + $pos = strrpos($input, '-'); + if (false !== $pos) { + $output = substr($input, 0, $pos++); + } else { + $pos = 0; + } + + $outputLength = \strlen($output); + $inputLength = \strlen($input); + + while ($pos < $inputLength) { + $oldi = $i; + $w = 1; + + for ($k = 36;; $k += 36) { + $digit = self::$decodeTable[$input[$pos++]]; + $i += $digit * $w; + $t = self::calculateThreshold($k, $bias); + + if ($digit < $t) { + break; + } + + $w *= 36 - $t; + } + + $bias = self::adapt($i - $oldi, ++$outputLength, 0 === $oldi); + $n = $n + (int) ($i / $outputLength); + $i = $i % $outputLength; + $output = mb_substr($output, 0, $i, 'utf-8').mb_chr($n, 'utf-8').mb_substr($output, $i, $outputLength - 1, 'utf-8'); + + ++$i; + } + + return $output; + } +} diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/symfony/polyfill-intl-idn/LICENSE similarity index 85% rename from vendor/paragonie/random_compat/LICENSE rename to vendor/symfony/polyfill-intl-idn/LICENSE index 45c7017..3f853aa 100644 --- a/vendor/paragonie/random_compat/LICENSE +++ b/vendor/symfony/polyfill-intl-idn/LICENSE @@ -1,13 +1,11 @@ -The MIT License (MIT) - -Copyright (c) 2015 Paragon Initiative Enterprises +Copyright (c) 2018-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. @@ -17,6 +15,5 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-intl-idn/README.md b/vendor/symfony/polyfill-intl-idn/README.md new file mode 100644 index 0000000..5fd8c6e --- /dev/null +++ b/vendor/symfony/polyfill-intl-idn/README.md @@ -0,0 +1,12 @@ +Symfony Polyfill / Intl: Idn +============================ + +This component provides `idn_to_ascii` and `idn_to_utf8` functions to users who run php versions without the intl extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-intl-idn/bootstrap.php b/vendor/symfony/polyfill-intl-idn/bootstrap.php new file mode 100644 index 0000000..c6e3921 --- /dev/null +++ b/vendor/symfony/polyfill-intl-idn/bootstrap.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Idn as p; + +if (!function_exists('idn_to_ascii')) { + define('U_IDNA_PROHIBITED_ERROR', 66560); + define('U_IDNA_ERROR_START', 66560); + define('U_IDNA_UNASSIGNED_ERROR', 66561); + define('U_IDNA_CHECK_BIDI_ERROR', 66562); + define('U_IDNA_STD3_ASCII_RULES_ERROR', 66563); + define('U_IDNA_ACE_PREFIX_ERROR', 66564); + define('U_IDNA_VERIFICATION_ERROR', 66565); + define('U_IDNA_LABEL_TOO_LONG_ERROR', 66566); + define('U_IDNA_ZERO_LENGTH_LABEL_ERROR', 66567); + define('U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR', 66568); + define('U_IDNA_ERROR_LIMIT', 66569); + define('U_STRINGPREP_PROHIBITED_ERROR', 66560); + define('U_STRINGPREP_UNASSIGNED_ERROR', 66561); + define('U_STRINGPREP_CHECK_BIDI_ERROR', 66562); + define('IDNA_DEFAULT', 0); + define('IDNA_ALLOW_UNASSIGNED', 1); + define('IDNA_USE_STD3_RULES', 2); + define('IDNA_CHECK_BIDI', 4); + define('IDNA_CHECK_CONTEXTJ', 8); + define('IDNA_NONTRANSITIONAL_TO_ASCII', 16); + define('IDNA_NONTRANSITIONAL_TO_UNICODE', 32); + define('INTL_IDNA_VARIANT_2003', 0); + define('INTL_IDNA_VARIANT_UTS46', 1); + define('IDNA_ERROR_EMPTY_LABEL', 1); + define('IDNA_ERROR_LABEL_TOO_LONG', 2); + define('IDNA_ERROR_DOMAIN_NAME_TOO_LONG', 4); + define('IDNA_ERROR_LEADING_HYPHEN', 8); + define('IDNA_ERROR_TRAILING_HYPHEN', 16); + define('IDNA_ERROR_HYPHEN_3_4', 32); + define('IDNA_ERROR_LEADING_COMBINING_MARK', 64); + define('IDNA_ERROR_DISALLOWED', 128); + define('IDNA_ERROR_PUNYCODE', 256); + define('IDNA_ERROR_LABEL_HAS_DOT', 512); + define('IDNA_ERROR_INVALID_ACE_LABEL', 1024); + define('IDNA_ERROR_BIDI', 2048); + define('IDNA_ERROR_CONTEXTJ', 4096); + + if (PHP_VERSION_ID < 70400) { + function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); } + function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); } + } else { + function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); } + function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); } + } +} diff --git a/vendor/symfony/polyfill-intl-idn/composer.json b/vendor/symfony/polyfill-intl-idn/composer.json new file mode 100644 index 0000000..403f4aa --- /dev/null +++ b/vendor/symfony/polyfill-intl-idn/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/polyfill-intl-idn", + "type": "library", + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "idn"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.10" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Intl\\Idn\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.14-dev" + } + } +} diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE index 24fa32c..4cd8bdd 100644 --- a/vendor/symfony/polyfill-mbstring/LICENSE +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2018 Fabien Potencier +Copyright (c) 2015-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php index a5e4a8f..15503bc 100644 --- a/vendor/symfony/polyfill-mbstring/Mbstring.php +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -35,6 +35,7 @@ * - mb_strlen - Get string length * - mb_strpos - Find position of first occurrence of string in a string * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_str_split - Convert a string to an array * - mb_strtolower - Make a string lowercase * - mb_strtoupper - Make a string uppercase * - mb_substitute_character - Set/Get substitution character @@ -511,7 +512,9 @@ public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = n $offset = 0; } elseif ($offset = (int) $offset) { if ($offset < 0) { - $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + if (0 > $offset += self::mb_strlen($needle)) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + } $offset = 0; } else { $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); @@ -523,6 +526,45 @@ public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = n return false !== $pos ? $offset + $pos : false; } + public static function mb_str_split($string, $split_length = 1, $encoding = null) + { + if (null !== $string && !\is_scalar($string) && !(\is_object($string) && \method_exists($string, '__toString'))) { + trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', E_USER_WARNING); + + return null; + } + + if (1 > $split_length = (int) $split_length) { + trigger_error('The length of each segment must be greater than zero', E_USER_WARNING); + + return false; + } + + if (null === $encoding) { + $encoding = mb_internal_encoding(); + } + + if ('UTF-8' === $encoding = self::getEncoding($encoding)) { + $rx = '/('; + while (65535 < $split_length) { + $rx .= '.{65535}'; + $split_length -= 65535; + } + $rx .= '.{'.$split_length.'})/us'; + + return preg_split($rx, $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + } + + $result = array(); + $length = mb_strlen($string, $encoding); + + for ($i = 0; $i < $length; $i += $split_length) { + $result[] = mb_substr($string, $i, $split_length, $encoding); + } + + return $result; + } + public static function mb_strtolower($s, $encoding = null) { return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); @@ -546,7 +588,7 @@ public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { - return substr($s, $start, null === $length ? 2147483647 : $length); + return (string) substr($s, $start, null === $length ? 2147483647 : $length); } if ($start < 0) { @@ -786,11 +828,16 @@ private static function getEncoding($encoding) return self::$internalEncoding; } + if ('UTF-8' === $encoding) { + return 'UTF-8'; + } + $encoding = strtoupper($encoding); if ('8BIT' === $encoding || 'BINARY' === $encoding) { return 'CP850'; } + if ('UTF8' === $encoding) { return 'UTF-8'; } diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php index 2fdcc5a..204a41b 100644 --- a/vendor/symfony/polyfill-mbstring/bootstrap.php +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -56,3 +56,7 @@ function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } } + +if (!function_exists('mb_str_split')) { + function mb_str_split($string, $split_length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $split_length, $encoding); } +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json index 50ea12f..1a8bec5 100644 --- a/vendor/symfony/polyfill-mbstring/composer.json +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } } } diff --git a/vendor/symfony/polyfill-php70/Php70.php b/vendor/symfony/polyfill-php70/Php70.php deleted file mode 100644 index 7f1ad08..0000000 --- a/vendor/symfony/polyfill-php70/Php70.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Polyfill\Php70; - -/** - * @author Nicolas Grekas - * - * @internal - */ -final class Php70 -{ - public static function intdiv($dividend, $divisor) - { - $dividend = self::intArg($dividend, __FUNCTION__, 1); - $divisor = self::intArg($divisor, __FUNCTION__, 2); - - if (0 === $divisor) { - throw new \DivisionByZeroError('Division by zero'); - } - if (-1 === $divisor && ~PHP_INT_MAX === $dividend) { - throw new \ArithmeticError('Division of PHP_INT_MIN by -1 is not an integer'); - } - - return ($dividend - ($dividend % $divisor)) / $divisor; - } - - public static function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) - { - $count = 0; - $result = (string) $subject; - if (0 === $limit = self::intArg($limit, __FUNCTION__, 3)) { - return $result; - } - - foreach ($patterns as $pattern => $callback) { - $result = preg_replace_callback($pattern, $callback, $result, $limit, $c); - $count += $c; - } - - return $result; - } - - public static function error_clear_last() - { - static $handler; - if (!$handler) { - $handler = function () { return false; }; - } - set_error_handler($handler); - @trigger_error(''); - restore_error_handler(); - } - - private static function intArg($value, $caller, $pos) - { - if (\is_int($value)) { - return $value; - } - if (!\is_numeric($value) || PHP_INT_MAX <= ($value += 0) || ~PHP_INT_MAX >= $value) { - throw new \TypeError(sprintf('%s() expects parameter %d to be integer, %s given', $caller, $pos, \gettype($value))); - } - - return (int) $value; - } -} diff --git a/vendor/symfony/polyfill-php70/README.md b/vendor/symfony/polyfill-php70/README.md deleted file mode 100644 index 04988c6..0000000 --- a/vendor/symfony/polyfill-php70/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Symfony Polyfill / Php70 -======================== - -This component provides features unavailable in releases prior to PHP 7.0: - -- [`intdiv`](http://php.net/intdiv) -- [`preg_replace_callback_array`](http://php.net/preg_replace_callback_array) -- [`error_clear_last`](http://php.net/error_clear_last) -- `random_bytes` and `random_int` (from [paragonie/random_compat](https://github.com/paragonie/random_compat)) -- [`*Error` throwable classes](http://php.net/Error) -- [`PHP_INT_MIN`](http://php.net/manual/en/reserved.constants.php#constant.php-int-min) -- `SessionUpdateTimestampHandlerInterface` - -More information can be found in the -[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). - -Compatibility notes -=================== - -To write portable code between PHP5 and PHP7, some care must be taken: -- `\*Error` exceptions must be caught before `\Exception`; -- after calling `error_clear_last()`, the result of `$e = error_get_last()` must be - verified using `isset($e['message'][0])` instead of `null !== $e`. - -License -======= - -This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php deleted file mode 100644 index 6819124..0000000 --- a/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php +++ /dev/null @@ -1,5 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Symfony\Polyfill\Php70 as p; - -if (PHP_VERSION_ID < 70000) { - if (!defined('PHP_INT_MIN')) { - define('PHP_INT_MIN', ~PHP_INT_MAX); - } - if (!function_exists('intdiv')) { - function intdiv($dividend, $divisor) { return p\Php70::intdiv($dividend, $divisor); } - } - if (!function_exists('preg_replace_callback_array')) { - function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) { return p\Php70::preg_replace_callback_array($patterns, $subject, $limit, $count); } - } - if (!function_exists('error_clear_last')) { - function error_clear_last() { return p\Php70::error_clear_last(); } - } -} diff --git a/vendor/symfony/polyfill-php72/LICENSE b/vendor/symfony/polyfill-php72/LICENSE new file mode 100644 index 0000000..4cd8bdd --- /dev/null +++ b/vendor/symfony/polyfill-php72/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-php72/Php72.php b/vendor/symfony/polyfill-php72/Php72.php new file mode 100644 index 0000000..d531e84 --- /dev/null +++ b/vendor/symfony/polyfill-php72/Php72.php @@ -0,0 +1,216 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php72; + +/** + * @author Nicolas Grekas + * @author Dariusz Rumiński + * + * @internal + */ +final class Php72 +{ + private static $hashMask; + + public static function utf8_encode($s) + { + $s .= $s; + $len = \strlen($s); + + for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) { + switch (true) { + case $s[$i] < "\x80": $s[$j] = $s[$i]; break; + case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break; + default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break; + } + } + + return substr($s, 0, $j); + } + + public static function utf8_decode($s) + { + $s = (string) $s; + $len = \strlen($s); + + for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) { + switch ($s[$i] & "\xF0") { + case "\xC0": + case "\xD0": + $c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F"); + $s[$j] = $c < 256 ? \chr($c) : '?'; + break; + + case "\xF0": + ++$i; + // no break + + case "\xE0": + $s[$j] = '?'; + $i += 2; + break; + + default: + $s[$j] = $s[$i]; + } + } + + return substr($s, 0, $j); + } + + public static function php_os_family() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + return 'Windows'; + } + + $map = array( + 'Darwin' => 'Darwin', + 'DragonFly' => 'BSD', + 'FreeBSD' => 'BSD', + 'NetBSD' => 'BSD', + 'OpenBSD' => 'BSD', + 'Linux' => 'Linux', + 'SunOS' => 'Solaris', + ); + + return isset($map[PHP_OS]) ? $map[PHP_OS] : 'Unknown'; + } + + public static function spl_object_id($object) + { + if (null === self::$hashMask) { + self::initHashMask(); + } + if (null === $hash = spl_object_hash($object)) { + return; + } + + return self::$hashMask ^ hexdec(substr($hash, 16 - \PHP_INT_SIZE, \PHP_INT_SIZE)); + } + + public static function sapi_windows_vt100_support($stream, $enable = null) + { + if (!\is_resource($stream)) { + trigger_error('sapi_windows_vt100_support() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING); + + return false; + } + + $meta = stream_get_meta_data($stream); + + if ('STDIO' !== $meta['stream_type']) { + trigger_error('sapi_windows_vt100_support() was not able to analyze the specified stream', E_USER_WARNING); + + return false; + } + + // We cannot actually disable vt100 support if it is set + if (false === $enable || !self::stream_isatty($stream)) { + return false; + } + + // The native function does not apply to stdin + $meta = array_map('strtolower', $meta); + $stdin = 'php://stdin' === $meta['uri'] || 'php://fd/0' === $meta['uri']; + + return !$stdin + && (false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM') + || 'Hyper' === getenv('TERM_PROGRAM')); + } + + public static function stream_isatty($stream) + { + if (!\is_resource($stream)) { + trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING); + + return false; + } + + if ('\\' === \DIRECTORY_SEPARATOR) { + $stat = @fstat($stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + return \function_exists('posix_isatty') && @posix_isatty($stream); + } + + private static function initHashMask() + { + $obj = (object) array(); + self::$hashMask = -1; + + // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below + $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush'); + foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) { + if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { + $frame['line'] = 0; + break; + } + } + if (!empty($frame['line'])) { + ob_start(); + debug_zval_dump($obj); + self::$hashMask = (int) substr(ob_get_clean(), 17); + } + + self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - \PHP_INT_SIZE, \PHP_INT_SIZE)); + } + + public static function mb_chr($code, $encoding = null) + { + if (0x80 > $code %= 0x200000) { + $s = \chr($code); + } elseif (0x800 > $code) { + $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } else { + $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } + + if ('UTF-8' !== $encoding) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if (null == $encoding) { + $s = mb_convert_encoding($s, 'UTF-8'); + } elseif ('UTF-8' !== $encoding) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + if (1 === \strlen($s)) { + return \ord($s); + } + + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; + if (0xF0 <= $code) { + return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; + } + if (0xE0 <= $code) { + return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; + } + if (0xC0 <= $code) { + return (($code - 0xC0) << 6) + $s[2] - 0x80; + } + + return $code; + } +} diff --git a/vendor/symfony/polyfill-php72/README.md b/vendor/symfony/polyfill-php72/README.md new file mode 100644 index 0000000..82c45f7 --- /dev/null +++ b/vendor/symfony/polyfill-php72/README.md @@ -0,0 +1,27 @@ +Symfony Polyfill / Php72 +======================== + +This component provides functions added to PHP 7.2 core: + +- [`spl_object_id`](https://php.net/spl_object_id) +- [`stream_isatty`](https://php.net/stream_isatty) + +On Windows only: + +- [`sapi_windows_vt100_support`](https://php.net/sapi_windows_vt100_support) + +Moved to core since 7.2 (was in the optional XML extension earlier): + +- [`utf8_encode`](https://php.net/utf8_encode) +- [`utf8_decode`](https://php.net/utf8_decode) + +Also, it provides a constant added to PHP 7.2: +- [`PHP_OS_FAMILY`](http://php.net/manual/en/reserved.constants.php#constant.php-os-family) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-php72/bootstrap.php b/vendor/symfony/polyfill-php72/bootstrap.php new file mode 100644 index 0000000..519056d --- /dev/null +++ b/vendor/symfony/polyfill-php72/bootstrap.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php72 as p; + +if (PHP_VERSION_ID < 70200) { + if ('\\' === DIRECTORY_SEPARATOR && !function_exists('sapi_windows_vt100_support')) { + function sapi_windows_vt100_support($stream, $enable = null) { return p\Php72::sapi_windows_vt100_support($stream, $enable); } + } + if (!function_exists('stream_isatty')) { + function stream_isatty($stream) { return p\Php72::stream_isatty($stream); } + } + if (!function_exists('utf8_encode')) { + function utf8_encode($s) { return p\Php72::utf8_encode($s); } + function utf8_decode($s) { return p\Php72::utf8_decode($s); } + } + if (!function_exists('spl_object_id')) { + function spl_object_id($s) { return p\Php72::spl_object_id($s); } + } + if (!defined('PHP_OS_FAMILY')) { + define('PHP_OS_FAMILY', p\Php72::php_os_family()); + } + if (!function_exists('mb_chr')) { + function mb_ord($s, $enc = null) { return p\Php72::mb_ord($s, $enc); } + function mb_chr($code, $enc = null) { return p\Php72::mb_chr($code, $enc); } + function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } + } +} diff --git a/vendor/symfony/polyfill-php70/composer.json b/vendor/symfony/polyfill-php72/composer.json similarity index 61% rename from vendor/symfony/polyfill-php70/composer.json rename to vendor/symfony/polyfill-php72/composer.json index 13dcf94..d2f8464 100644 --- a/vendor/symfony/polyfill-php70/composer.json +++ b/vendor/symfony/polyfill-php72/composer.json @@ -1,7 +1,7 @@ { - "name": "symfony/polyfill-php70", + "name": "symfony/polyfill-php72", "type": "library", - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "keywords": ["polyfill", "shim", "compatibility", "portable"], "homepage": "https://symfony.com", "license": "MIT", @@ -16,18 +16,16 @@ } ], "require": { - "php": ">=5.3.3", - "paragonie/random_compat": "~1.0|~2.0|~9.99" + "php": ">=5.3.3" }, "autoload": { - "psr-4": { "Symfony\\Polyfill\\Php70\\": "" }, - "files": [ "bootstrap.php" ], - "classmap": [ "Resources/stubs" ] + "psr-4": { "Symfony\\Polyfill\\Php72\\": "" }, + "files": [ "bootstrap.php" ] }, "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } } } diff --git a/vendor/symfony/var-dumper/.gitattributes b/vendor/symfony/var-dumper/.gitattributes new file mode 100644 index 0000000..ebb9287 --- /dev/null +++ b/vendor/symfony/var-dumper/.gitattributes @@ -0,0 +1,3 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitignore export-ignore diff --git a/vendor/symfony/var-dumper/.gitignore b/vendor/symfony/var-dumper/.gitignore deleted file mode 100644 index 5414c2c..0000000 --- a/vendor/symfony/var-dumper/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -composer.lock -phpunit.xml -vendor/ diff --git a/vendor/symfony/var-dumper/CHANGELOG.md b/vendor/symfony/var-dumper/CHANGELOG.md index 2d44cad..94b1c17 100644 --- a/vendor/symfony/var-dumper/CHANGELOG.md +++ b/vendor/symfony/var-dumper/CHANGELOG.md @@ -1,6 +1,46 @@ CHANGELOG ========= +4.4.0 +----- + + * added `VarDumperTestTrait::setUpVarDumper()` and `VarDumperTestTrait::tearDownVarDumper()` + to configure casters & flags to use in tests + * added `ImagineCaster` and infrastructure to dump images + * added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data + * added `UuidCaster` + * made all casters final + * added support for the `NO_COLOR` env var (https://no-color.org/) + +4.3.0 +----- + + * added `DsCaster` to support dumping the contents of data structures from the Ds extension + +4.2.0 +----- + + * support selecting the format to use by setting the environment variable `VAR_DUMPER_FORMAT` to `html` or `cli` + +4.1.0 +----- + + * added a `ServerDumper` to send serialized Data clones to a server + * added a `ServerDumpCommand` and `DumpServer` to run a server collecting + and displaying dumps on a single place with multiple formats support + * added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support + +4.0.0 +----- + + * support for passing `\ReflectionClass` instances to the `Caster::castObject()` + method has been dropped, pass class names as strings instead + * the `Data::getRawData()` method has been removed + * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$filter = 0` + argument and moves `$message = ''` argument at 4th position. + * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$filter = 0` + argument and moves `$message = ''` argument at 4th position. + 3.4.0 ----- @@ -10,4 +50,4 @@ CHANGELOG 2.7.0 ----- - * deprecated Cloner\Data::getLimitedClone(). Use withMaxDepth, withMaxItemsPerDepth or withRefHandles instead. + * deprecated `Cloner\Data::getLimitedClone()`. Use `withMaxDepth`, `withMaxItemsPerDepth` or `withRefHandles` instead. diff --git a/vendor/symfony/var-dumper/Caster/AmqpCaster.php b/vendor/symfony/var-dumper/Caster/AmqpCaster.php index 655262f..1c6a42c 100644 --- a/vendor/symfony/var-dumper/Caster/AmqpCaster.php +++ b/vendor/symfony/var-dumper/Caster/AmqpCaster.php @@ -17,10 +17,12 @@ * Casts Amqp related classes to array representation. * * @author Grégoire Pineau + * + * @final since Symfony 4.4 */ class AmqpCaster { - private static $flags = array( + private static $flags = [ AMQP_DURABLE => 'AMQP_DURABLE', AMQP_PASSIVE => 'AMQP_PASSIVE', AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', @@ -35,22 +37,22 @@ class AmqpCaster AMQP_MULTIPLE => 'AMQP_MULTIPLE', AMQP_NOWAIT => 'AMQP_NOWAIT', AMQP_REQUEUE => 'AMQP_REQUEUE', - ); + ]; - private static $exchangeTypes = array( + private static $exchangeTypes = [ AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', - ); + ]; public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, $isNested) { $prefix = Caster::PREFIX_VIRTUAL; - $a += array( + $a += [ $prefix.'is_connected' => $c->isConnected(), - ); + ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPConnection\x00login"])) { @@ -64,7 +66,7 @@ public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, $timeout = $c->getTimeout(); } - $a += array( + $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'login' => $c->getLogin(), $prefix.'password' => $c->getPassword(), @@ -72,7 +74,7 @@ public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, $prefix.'vhost' => $c->getVhost(), $prefix.'port' => $c->getPort(), $prefix.'read_timeout' => $timeout, - ); + ]; return $a; } @@ -81,21 +83,21 @@ public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, $isNes { $prefix = Caster::PREFIX_VIRTUAL; - $a += array( + $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'channel_id' => $c->getChannelId(), - ); + ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPChannel\x00connection"])) { return $a; } - $a += array( + $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'prefetch_size' => $c->getPrefetchSize(), $prefix.'prefetch_count' => $c->getPrefetchCount(), - ); + ]; return $a; } @@ -104,21 +106,21 @@ public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, $isNested) { $prefix = Caster::PREFIX_VIRTUAL; - $a += array( + $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), - ); + ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPQueue\x00name"])) { return $a; } - $a += array( + $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'arguments' => $c->getArguments(), - ); + ]; return $a; } @@ -127,9 +129,9 @@ public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, $isN { $prefix = Caster::PREFIX_VIRTUAL; - $a += array( + $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), - ); + ]; $type = isset(self::$exchangeTypes[$c->getType()]) ? new ConstStub(self::$exchangeTypes[$c->getType()], $c->getType()) : $c->getType(); @@ -140,13 +142,13 @@ public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, $isN return $a; } - $a += array( + $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'type' => $type, $prefix.'arguments' => $c->getArguments(), - ); + ]; return $a; } @@ -165,10 +167,10 @@ public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, $isN } if (!($filter & Caster::EXCLUDE_VERBOSE)) { - $a += array($prefix.'body' => $c->getBody()); + $a += [$prefix.'body' => $c->getBody()]; } - $a += array( + $a += [ $prefix.'delivery_tag' => $c->getDeliveryTag(), $prefix.'is_redelivery' => $c->isRedelivery(), $prefix.'exchange_name' => $c->getExchangeName(), @@ -186,14 +188,14 @@ public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, $isN $prefix.'type' => $c->getType(), $prefix.'user_id' => $c->getUserId(), $prefix.'app_id' => $c->getAppId(), - ); + ]; return $a; } - private static function extractFlags($flags) + private static function extractFlags(int $flags): ConstStub { - $flagsArray = array(); + $flagsArray = []; foreach (self::$flags as $value => $name) { if ($flags & $value) { @@ -202,7 +204,7 @@ private static function extractFlags($flags) } if (!$flagsArray) { - $flagsArray = array('AMQP_NOPARAM'); + $flagsArray = ['AMQP_NOPARAM']; } return new ConstStub(implode('|', $flagsArray), $flags); diff --git a/vendor/symfony/var-dumper/Caster/ArgsStub.php b/vendor/symfony/var-dumper/Caster/ArgsStub.php index 7cd759a..591c7e2 100644 --- a/vendor/symfony/var-dumper/Caster/ArgsStub.php +++ b/vendor/symfony/var-dumper/Caster/ArgsStub.php @@ -20,13 +20,13 @@ */ class ArgsStub extends EnumStub { - private static $parameters = array(); + private static $parameters = []; - public function __construct(array $args, $function, $class) + public function __construct(array $args, string $function, ?string $class) { list($variadic, $params) = self::getParameters($function, $class); - $values = array(); + $values = []; foreach ($args as $k => $v) { $values[$k] = !is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; } @@ -41,7 +41,7 @@ public function __construct(array $args, $function, $class) $values[] = new EnumStub(array_splice($values, \count($params)), false); $params[] = $variadic; } - if (array('...') === $params) { + if (['...'] === $params) { $this->dumpKeys = false; $this->value = $values[0]->value; } else { @@ -49,7 +49,7 @@ public function __construct(array $args, $function, $class) } } - private static function getParameters($function, $class) + private static function getParameters(string $function, ?string $class): array { if (isset(self::$parameters[$k = $class.'::'.$function])) { return self::$parameters[$k]; @@ -58,23 +58,23 @@ private static function getParameters($function, $class) try { $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); } catch (\ReflectionException $e) { - return array(null, null); + return [null, null]; } $variadic = '...'; - $params = array(); + $params = []; foreach ($r->getParameters() as $v) { $k = '$'.$v->name; if ($v->isPassedByReference()) { $k = '&'.$k; } - if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + if ($v->isVariadic()) { $variadic .= $k; } else { $params[] = $k; } } - return self::$parameters[$k] = array($variadic, $params); + return self::$parameters[$k] = [$variadic, $params]; } } diff --git a/vendor/symfony/var-dumper/Caster/Caster.php b/vendor/symfony/var-dumper/Caster/Caster.php index 30cb9fc..81e238c 100644 --- a/vendor/symfony/var-dumper/Caster/Caster.php +++ b/vendor/symfony/var-dumper/Caster/Caster.php @@ -41,39 +41,37 @@ class Caster * Casts objects to arrays and adds the dynamic property prefix. * * @param object $obj The object to cast - * @param string $class The class of the object * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not * * @return array The array-cast of the object, with prefixed dynamic properties */ - public static function castObject($obj, $class, $hasDebugInfo = false) + public static function castObject($obj, string $class, bool $hasDebugInfo = false): array { - if ($class instanceof \ReflectionClass) { - @trigger_error(sprintf('Passing a ReflectionClass to "%s()" is deprecated since Symfony 3.3 and will be unsupported in 4.0. Pass the class name as string instead.', __METHOD__), E_USER_DEPRECATED); - $hasDebugInfo = $class->hasMethod('__debugInfo'); - $class = $class->name; - } if ($hasDebugInfo) { - $a = $obj->__debugInfo(); - } elseif ($obj instanceof \Closure) { - $a = array(); - } else { - $a = (array) $obj; + try { + $debugInfo = $obj->__debugInfo(); + } catch (\Exception $e) { + // ignore failing __debugInfo() + $hasDebugInfo = false; + } } + + $a = $obj instanceof \Closure ? [] : (array) $obj; + if ($obj instanceof \__PHP_Incomplete_Class) { return $a; } if ($a) { - static $publicProperties = array(); + static $publicProperties = []; $i = 0; - $prefixedKeys = array(); + $prefixedKeys = []; foreach ($a as $k => $v) { if (isset($k[0]) ? "\0" !== $k[0] : \PHP_VERSION_ID >= 70200) { if (!isset($publicProperties[$class])) { - foreach (get_class_vars($class) as $prop => $v) { - $publicProperties[$class][$prop] = true; + foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { + $publicProperties[$class][$prop->name] = true; } } if (!isset($publicProperties[$class][$k])) { @@ -93,6 +91,17 @@ public static function castObject($obj, $class, $hasDebugInfo = false) } } + if ($hasDebugInfo && \is_array($debugInfo)) { + foreach ($debugInfo as $k => $v) { + if (!isset($k[0]) || "\0" !== $k[0]) { + $k = self::PREFIX_VIRTUAL.$k; + } + + unset($a[$k]); + $a[$k] = $v; + } + } + return $a; } @@ -109,7 +118,7 @@ public static function castObject($obj, $class, $hasDebugInfo = false) * * @return array The filtered array */ - public static function filter(array $a, $filter, array $listedProperties = array(), &$count = 0) + public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array { $count = 0; @@ -119,7 +128,7 @@ public static function filter(array $a, $filter, array $listedProperties = array if (null === $v) { $type |= self::EXCLUDE_NULL & $filter; $type |= self::EXCLUDE_EMPTY & $filter; - } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || array() === $v) { + } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { $type |= self::EXCLUDE_EMPTY & $filter; } if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { @@ -150,7 +159,7 @@ public static function filter(array $a, $filter, array $listedProperties = array return $a; } - public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, $isNested) + public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array { if (isset($a['__PHP_Incomplete_Class_Name'])) { $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; diff --git a/vendor/symfony/var-dumper/Caster/ClassStub.php b/vendor/symfony/var-dumper/Caster/ClassStub.php index 859c1ec..c998b49 100644 --- a/vendor/symfony/var-dumper/Caster/ClassStub.php +++ b/vendor/symfony/var-dumper/Caster/ClassStub.php @@ -11,6 +11,8 @@ namespace Symfony\Component\VarDumper\Caster; +use Symfony\Component\VarDumper\Cloner\Stub; + /** * Represents a PHP class identifier. * @@ -22,31 +24,25 @@ class ClassStub extends ConstStub * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier */ - public function __construct($identifier, $callable = null) + public function __construct(string $identifier, $callable = null) { $this->value = $identifier; - if (0 < $i = strrpos($identifier, '\\')) { - $this->attr['ellipsis'] = \strlen($identifier) - $i; - $this->attr['ellipsis-type'] = 'class'; - $this->attr['ellipsis-tail'] = 1; - } - try { if (null !== $callable) { if ($callable instanceof \Closure) { $r = new \ReflectionFunction($callable); } elseif (\is_object($callable)) { - $r = array($callable, '__invoke'); + $r = [$callable, '__invoke']; } elseif (\is_array($callable)) { $r = $callable; } elseif (false !== $i = strpos($callable, '::')) { - $r = array(substr($callable, 0, $i), substr($callable, 2 + $i)); + $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; } else { $r = new \ReflectionFunction($callable); } } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { - $r = array(substr($identifier, 0, $i), substr($identifier, 2 + $i)); + $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; } else { $r = new \ReflectionClass($identifier); } @@ -58,8 +54,31 @@ public function __construct($identifier, $callable = null) $r = new \ReflectionClass($r[0]); } } + + if (false !== strpos($identifier, "class@anonymous\0")) { + $this->value = $identifier = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0]; + }, $identifier); + } + + if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { + $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); + $s = ReflectionCaster::getSignature($s); + + if ('()' === substr($identifier, -2)) { + $this->value = substr_replace($identifier, $s, -2); + } else { + $this->value .= $s; + } + } } catch (\ReflectionException $e) { return; + } finally { + if (0 < $i = strrpos($this->value, '\\')) { + $this->attr['ellipsis'] = \strlen($this->value) - $i; + $this->attr['ellipsis-type'] = 'class'; + $this->attr['ellipsis-tail'] = 1; + } } if ($f = $r->getFileName()) { @@ -75,9 +94,9 @@ public static function wrapCallable($callable) } if (!\is_array($callable)) { - $callable = new static($callable); + $callable = new static($callable, $callable); } elseif (\is_string($callable[0])) { - $callable[0] = new static($callable[0]); + $callable[0] = new static($callable[0], $callable); } else { $callable[1] = new static($callable[1], $callable); } diff --git a/vendor/symfony/var-dumper/Caster/ConstStub.php b/vendor/symfony/var-dumper/Caster/ConstStub.php index 26c0010..8b01797 100644 --- a/vendor/symfony/var-dumper/Caster/ConstStub.php +++ b/vendor/symfony/var-dumper/Caster/ConstStub.php @@ -20,12 +20,15 @@ */ class ConstStub extends Stub { - public function __construct($name, $value) + public function __construct(string $name, $value = null) { $this->class = $name; - $this->value = $value; + $this->value = 1 < \func_num_args() ? $value : $name; } + /** + * @return string + */ public function __toString() { return (string) $this->value; diff --git a/vendor/symfony/var-dumper/Caster/CutStub.php b/vendor/symfony/var-dumper/Caster/CutStub.php index 690338f..464c6db 100644 --- a/vendor/symfony/var-dumper/Caster/CutStub.php +++ b/vendor/symfony/var-dumper/Caster/CutStub.php @@ -28,6 +28,11 @@ public function __construct($value) case 'object': $this->type = self::TYPE_OBJECT; $this->class = \get_class($value); + + if ($value instanceof \Closure) { + ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); + } + $this->cut = -1; break; diff --git a/vendor/symfony/var-dumper/Caster/DOMCaster.php b/vendor/symfony/var-dumper/Caster/DOMCaster.php index 3a99865..41e52d6 100644 --- a/vendor/symfony/var-dumper/Caster/DOMCaster.php +++ b/vendor/symfony/var-dumper/Caster/DOMCaster.php @@ -17,10 +17,12 @@ * Casts DOM related classes to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class DOMCaster { - private static $errorCodes = array( + private static $errorCodes = [ DOM_PHP_ERR => 'DOM_PHP_ERR', DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', @@ -38,9 +40,9 @@ class DOMCaster DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', - ); + ]; - private static $nodeTypes = array( + private static $nodeTypes = [ XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', XML_TEXT_NODE => 'XML_TEXT_NODE', @@ -59,7 +61,7 @@ class DOMCaster XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', - ); + ]; public static function castException(\DOMException $e, array $a, Stub $stub, $isNested) { @@ -73,26 +75,26 @@ public static function castException(\DOMException $e, array $a, Stub $stub, $is public static function castLength($dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'length' => $dom->length, - ); + ]; return $a; } public static function castImplementation($dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ Caster::PREFIX_VIRTUAL.'Core' => '1.0', Caster::PREFIX_VIRTUAL.'XML' => '2.0', - ); + ]; return $a; } public static function castNode(\DOMNode $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'nodeName' => $dom->nodeName, 'nodeValue' => new CutStub($dom->nodeValue), 'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType), @@ -109,14 +111,14 @@ public static function castNode(\DOMNode $dom, array $a, Stub $stub, $isNested) 'localName' => $dom->localName, 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, 'textContent' => new CutStub($dom->textContent), - ); + ]; return $a; } public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'nodeName' => $dom->nodeName, 'nodeValue' => new CutStub($dom->nodeValue), 'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType), @@ -125,14 +127,14 @@ public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub 'namespaceURI' => $dom->namespaceURI, 'ownerDocument' => new CutStub($dom->ownerDocument), 'parentNode' => new CutStub($dom->parentNode), - ); + ]; return $a; } public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $isNested, $filter = 0) { - $a += array( + $a += [ 'doctype' => $dom->doctype, 'implementation' => $dom->implementation, 'documentElement' => new CutStub($dom->documentElement), @@ -152,12 +154,12 @@ public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $is 'preserveWhiteSpace' => $dom->preserveWhiteSpace, 'recover' => $dom->recover, 'substituteEntities' => $dom->substituteEntities, - ); + ]; if (!($filter & Caster::EXCLUDE_VERBOSE)) { $formatOutput = $dom->formatOutput; $dom->formatOutput = true; - $a += array(Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()); + $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; $dom->formatOutput = $formatOutput; } @@ -166,136 +168,136 @@ public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $is public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'data' => $dom->data, 'length' => $dom->length, - ); + ]; return $a; } public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'name' => $dom->name, 'specified' => $dom->specified, 'value' => $dom->value, 'ownerElement' => $dom->ownerElement, 'schemaTypeInfo' => $dom->schemaTypeInfo, - ); + ]; return $a; } public static function castElement(\DOMElement $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'tagName' => $dom->tagName, 'schemaTypeInfo' => $dom->schemaTypeInfo, - ); + ]; return $a; } public static function castText(\DOMText $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'wholeText' => $dom->wholeText, - ); + ]; return $a; } public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'typeName' => $dom->typeName, 'typeNamespace' => $dom->typeNamespace, - ); + ]; return $a; } public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'severity' => $dom->severity, 'message' => $dom->message, 'type' => $dom->type, 'relatedException' => $dom->relatedException, 'related_data' => $dom->related_data, 'location' => $dom->location, - ); + ]; return $a; } public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'lineNumber' => $dom->lineNumber, 'columnNumber' => $dom->columnNumber, 'offset' => $dom->offset, 'relatedNode' => $dom->relatedNode, 'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri, - ); + ]; return $a; } public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'name' => $dom->name, 'entities' => $dom->entities, 'notations' => $dom->notations, 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, 'internalSubset' => $dom->internalSubset, - ); + ]; return $a; } public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, - ); + ]; return $a; } public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, 'notationName' => $dom->notationName, 'actualEncoding' => $dom->actualEncoding, 'encoding' => $dom->encoding, 'version' => $dom->version, - ); + ]; return $a; } public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'target' => $dom->target, 'data' => $dom->data, - ); + ]; return $a; } public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ 'document' => $dom->document, - ); + ]; return $a; } diff --git a/vendor/symfony/var-dumper/Caster/DateCaster.php b/vendor/symfony/var-dumper/Caster/DateCaster.php index 175d986..6b264c7 100644 --- a/vendor/symfony/var-dumper/Caster/DateCaster.php +++ b/vendor/symfony/var-dumper/Caster/DateCaster.php @@ -17,9 +17,13 @@ * Casts DateTimeInterface related classes to array representation. * * @author Dany Maillard + * + * @final since Symfony 4.4 */ class DateCaster { + private const PERIOD_LIMIT = 3; + public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter) { $prefix = Caster::PREFIX_VIRTUAL; @@ -31,7 +35,11 @@ public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') ; - $a = array(); + unset( + $a[Caster::PREFIX_DYNAMIC.'date'], + $a[Caster::PREFIX_DYNAMIC.'timezone'], + $a[Caster::PREFIX_DYNAMIC.'timezone_type'] + ); $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); $stub->class .= $d->format(' @U'); @@ -45,12 +53,12 @@ public static function castInterval(\DateInterval $interval, array $a, Stub $stu $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; - $i = array(Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)); + $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; } - private static function formatInterval(\DateInterval $i) + private static function formatInterval(\DateInterval $i): string { $format = '%R '; @@ -61,12 +69,7 @@ private static function formatInterval(\DateInterval $i) $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); } - if (\PHP_VERSION_ID >= 70100 && isset($i->f)) { - $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; - } else { - $format .= $i->h || $i->i || $i->s ? '%H:%I:%S' : ''; - } - + $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; $format = '%R ' === $format ? '0s' : $format; return $i->format(rtrim($format)); @@ -76,26 +79,22 @@ public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stu { $location = $timeZone->getLocation(); $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P'); - $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code'], \Locale::getDefault()) : ''; + $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; - $z = array(Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)); + $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; } public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNested, $filter) { - if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { // see https://bugs.php.net/bug.php?id=71635 - return $a; - } - - $dates = array(); - if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/bug.php?id=74639 + $dates = []; + if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/74639 foreach (clone $p as $i => $d) { - if (3 === $i) { + if (self::PERIOD_LIMIT === $i) { $now = new \DateTimeImmutable(); $dates[] = sprintf('%s more', ($end = $p->getEndDate()) - ? ceil(($end->format('U.u') - $d->format('U.u')) / ($now->add($p->getDateInterval())->format('U.u') - $now->format('U.u'))) + ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) : $p->recurrences - $i ); break; @@ -112,17 +111,17 @@ public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNeste ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s' ); - $p = array(Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))); + $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; } - private static function formatDateTime(\DateTimeInterface $d, $extra = '') + private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string { return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); } - private static function formatSeconds($s, $us) + private static function formatSeconds(string $s, string $us): string { return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); } diff --git a/vendor/symfony/var-dumper/Caster/DoctrineCaster.php b/vendor/symfony/var-dumper/Caster/DoctrineCaster.php index 785d027..7409508 100644 --- a/vendor/symfony/var-dumper/Caster/DoctrineCaster.php +++ b/vendor/symfony/var-dumper/Caster/DoctrineCaster.php @@ -20,13 +20,15 @@ * Casts Doctrine related classes to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class DoctrineCaster { public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, $isNested) { - foreach (array('__cloner__', '__initializer__') as $k) { - if (array_key_exists($k, $a)) { + foreach (['__cloner__', '__initializer__'] as $k) { + if (\array_key_exists($k, $a)) { unset($a[$k]); ++$stub->cut; } @@ -37,8 +39,8 @@ public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNested) { - foreach (array('_entityPersister', '_identifier') as $k) { - if (array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { + foreach (['_entityPersister', '_identifier'] as $k) { + if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { unset($a[$k]); ++$stub->cut; } @@ -49,8 +51,8 @@ public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNe public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, $isNested) { - foreach (array('snapshot', 'association', 'typeClass') as $k) { - if (array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { + foreach (['snapshot', 'association', 'typeClass'] as $k) { + if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { $a[$k] = new CutStub($a[$k]); } } diff --git a/vendor/symfony/var-dumper/Caster/DsCaster.php b/vendor/symfony/var-dumper/Caster/DsCaster.php new file mode 100644 index 0000000..11423c9 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DsCaster.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Ds\Collection; +use Ds\Map; +use Ds\Pair; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts Ds extension classes to array representation. + * + * @author Jáchym Toušek + * + * @final since Symfony 4.4 + */ +class DsCaster +{ + public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array + { + $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count(); + $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity(); + + if (!$c instanceof Map) { + $a += $c->toArray(); + } + + return $a; + } + + public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array + { + foreach ($c as $k => $v) { + $a[] = new DsPairStub($k, $v); + } + + return $a; + } + + public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array + { + foreach ($c->toArray() as $k => $v) { + $a[Caster::PREFIX_VIRTUAL.$k] = $v; + } + + return $a; + } + + public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array + { + if ($isNested) { + $stub->class = Pair::class; + $stub->value = null; + $stub->handle = 0; + + $a = $c->value; + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/DsPairStub.php b/vendor/symfony/var-dumper/Caster/DsPairStub.php new file mode 100644 index 0000000..a1dcc15 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DsPairStub.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Nicolas Grekas + */ +class DsPairStub extends Stub +{ + public function __construct($key, $value) + { + $this->value = [ + Caster::PREFIX_VIRTUAL.'key' => $key, + Caster::PREFIX_VIRTUAL.'value' => $value, + ]; + } +} diff --git a/vendor/symfony/var-dumper/Caster/EnumStub.php b/vendor/symfony/var-dumper/Caster/EnumStub.php index 3cee23e..7a4e98a 100644 --- a/vendor/symfony/var-dumper/Caster/EnumStub.php +++ b/vendor/symfony/var-dumper/Caster/EnumStub.php @@ -22,7 +22,7 @@ class EnumStub extends Stub { public $dumpKeys = true; - public function __construct(array $values, $dumpKeys = true) + public function __construct(array $values, bool $dumpKeys = true) { $this->value = $values; $this->dumpKeys = $dumpKeys; diff --git a/vendor/symfony/var-dumper/Caster/ExceptionCaster.php b/vendor/symfony/var-dumper/Caster/ExceptionCaster.php index 49df71d..9fe1e39 100644 --- a/vendor/symfony/var-dumper/Caster/ExceptionCaster.php +++ b/vendor/symfony/var-dumper/Caster/ExceptionCaster.php @@ -11,7 +11,7 @@ namespace Symfony\Component\VarDumper\Caster; -use Symfony\Component\Debug\Exception\SilencedErrorContext; +use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; @@ -19,12 +19,14 @@ * Casts common Exception classes to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class ExceptionCaster { public static $srcContext = 1; public static $traceArgs = true; - public static $errorTypes = array( + public static $errorTypes = [ E_DEPRECATED => 'E_DEPRECATED', E_USER_DEPRECATED => 'E_USER_DEPRECATED', E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', @@ -40,9 +42,9 @@ class ExceptionCaster E_USER_WARNING => 'E_USER_WARNING', E_USER_NOTICE => 'E_USER_NOTICE', E_STRICT => 'E_STRICT', - ); + ]; - private static $framesCache = array(); + private static $framesCache = []; public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0) { @@ -71,7 +73,9 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { $b = (array) $a[$xPrefix.'previous']; - self::traceUnshift($b[$xPrefix.'trace'], \get_class($a[$xPrefix.'previous']), $b[$prefix.'file'], $b[$prefix.'line']); + $class = \get_class($a[$xPrefix.'previous']); + $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; + self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']); $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); } @@ -92,10 +96,10 @@ public static function castSilencedErrorContext(SilencedErrorContext $e, array $ $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } - $trace = array(array( + $trace = [[ 'file' => $a[$sPrefix.'file'], 'line' => $a[$sPrefix.'line'], - )); + ]]; if (isset($a[$sPrefix.'trace'])) { $trace = array_merge($trace, $a[$sPrefix.'trace']); @@ -117,16 +121,16 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is $frames = $trace->value; $prefix = Caster::PREFIX_VIRTUAL; - $a = array(); + $a = []; $j = \count($frames); if (0 > $i = $trace->sliceOffset) { $i = max(0, $j + $i); } if (!isset($trace->value[$i])) { - return array(); + return []; } $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; - $frames[] = array('function' => ''); + $frames[] = ['function' => '']; $collapse = false; for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { @@ -134,16 +138,16 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; $frame = new FrameStub( - array( + [ 'object' => isset($f['object']) ? $f['object'] : null, 'class' => isset($f['class']) ? $f['class'] : null, 'type' => isset($f['type']) ? $f['type'] : null, 'function' => isset($f['function']) ? $f['function'] : null, - ) + $frames[$i - 1], + ] + $frames[$i - 1], false, true ); - $f = self::castFrameStub($frame, array(), $frame, true); + $f = self::castFrameStub($frame, [], $frame, true); if (isset($f[$prefix.'src'])) { foreach ($f[$prefix.'src']->value as $label => $frame) { if (0 === strpos($label, "\0~collapse=0")) { @@ -202,7 +206,6 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is $f['file'] = substr($f['file'], 0, -\strlen($match[0])); $f['line'] = (int) $match[1]; } - $caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null; $src = $f['line']; $srcKey = $f['file']; $ellipsis = new LinkStub($srcKey, 0); @@ -222,24 +225,24 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is $templatePath = null; } if ($templateSrc) { - $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath); + $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; } } } if ($srcKey == $f['file']) { - $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, $caller, 'php', $f['file']); + $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); $srcKey .= ':'.$f['line']; if ($ellipsis) { $ellipsis += 1 + \strlen($f['line']); } } - $srcAttr .= '&separator= '; + $srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']); } else { $srcAttr .= '&separator=:'; } $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; - self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src)); + self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); } } @@ -259,13 +262,13 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is return $a; } - private static function filterExceptionArray($xClass, array $a, $xPrefix, $filter) + private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array { if (isset($a[$xPrefix.'trace'])) { $trace = $a[$xPrefix.'trace']; unset($a[$xPrefix.'trace']); // Ensures the trace is always last } else { - $trace = array(); + $trace = []; } if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { @@ -279,6 +282,12 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte } unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']); + if (isset($a[Caster::PREFIX_PROTECTED.'message']) && false !== strpos($a[Caster::PREFIX_PROTECTED.'message'], "class@anonymous\0")) { + $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0]; + }, $a[Caster::PREFIX_PROTECTED.'message']); + } + if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } @@ -286,28 +295,53 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte return $a; } - private static function traceUnshift(&$trace, $class, $file, $line) + private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void { if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { return; } - array_unshift($trace, array( + array_unshift($trace, [ 'function' => $class ? 'new '.$class : null, 'file' => $file, 'line' => $line, - )); + ]); } - private static function extractSource($srcLines, $line, $srcContext, $title, $lang, $file = null) + private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub { $srcLines = explode("\n", $srcLines); - $src = array(); + $src = []; for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { $src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n"; } - $srcLines = array(); + if ($frame['function'] ?? false) { + $stub = new CutStub(new \stdClass()); + $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; + $stub->type = Stub::TYPE_OBJECT; + $stub->attr['cut_hash'] = true; + $stub->attr['file'] = $frame['file']; + $stub->attr['line'] = $frame['line']; + + try { + $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); + $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); + + if ($f = $caller->getFileName()) { + $stub->attr['file'] = $f; + $stub->attr['line'] = $caller->getStartLine(); + } + } catch (\ReflectionException $e) { + // ignore fake class/function + } + + $srcLines = ["\0~separator=\0" => $stub]; + } else { + $stub = null; + $srcLines = []; + } + $ltrim = 0; do { $pad = null; @@ -334,7 +368,7 @@ private static function extractSource($srcLines, $line, $srcContext, $title, $la if ($i !== $srcContext) { $c = new ConstStub('default', $c); } else { - $c = new ConstStub($c, $title); + $c = new ConstStub($c, $stub ? 'in '.$stub->class : ''); if (null !== $file) { $c->attr['file'] = $file; $c->attr['line'] = $line; diff --git a/vendor/symfony/var-dumper/Caster/FrameStub.php b/vendor/symfony/var-dumper/Caster/FrameStub.php index 1e1194d..8786755 100644 --- a/vendor/symfony/var-dumper/Caster/FrameStub.php +++ b/vendor/symfony/var-dumper/Caster/FrameStub.php @@ -21,7 +21,7 @@ class FrameStub extends EnumStub public $keepArgs; public $inTraceStub; - public function __construct(array $frame, $keepArgs = true, $inTraceStub = false) + public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false) { $this->value = $frame; $this->keepArgs = $keepArgs; diff --git a/vendor/symfony/var-dumper/Caster/GmpCaster.php b/vendor/symfony/var-dumper/Caster/GmpCaster.php new file mode 100644 index 0000000..2b20e15 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/GmpCaster.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts GMP objects to array representation. + * + * @author Hamza Amrouche + * @author Nicolas Grekas + * + * @final since Symfony 4.4 + */ +class GmpCaster +{ + public static function castGmp(\GMP $gmp, array $a, Stub $stub, $isNested, $filter): array + { + $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/ImagineCaster.php b/vendor/symfony/var-dumper/Caster/ImagineCaster.php new file mode 100644 index 0000000..d1289da --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ImagineCaster.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Imagine\Image\ImageInterface; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Grégoire Pineau + */ +final class ImagineCaster +{ + public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array + { + $imgData = $c->get('png'); + if (\strlen($imgData) > 1 * 1000 * 1000) { + $a += [ + Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), + ]; + } else { + $a += [ + Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), + ]; + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/ImgStub.php b/vendor/symfony/var-dumper/Caster/ImgStub.php new file mode 100644 index 0000000..05789fe --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ImgStub.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * @author Grégoire Pineau + */ +class ImgStub extends ConstStub +{ + public function __construct(string $data, string $contentType, string $size) + { + $this->value = ''; + $this->attr['img-data'] = $data; + $this->attr['img-size'] = $size; + $this->attr['content-type'] = $contentType; + } +} diff --git a/vendor/symfony/var-dumper/Caster/IntlCaster.php b/vendor/symfony/var-dumper/Caster/IntlCaster.php new file mode 100644 index 0000000..d7099cb --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/IntlCaster.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Nicolas Grekas + * @author Jan Schädlich + * + * @final since Symfony 4.4 + */ +class IntlCaster +{ + public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, $isNested) + { + $a += [ + Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), + Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), + ]; + + return self::castError($c, $a); + } + + public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $a += [ + Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), + Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), + ]; + + if ($filter & Caster::EXCLUDE_VERBOSE) { + $stub->cut += 3; + + return self::castError($c, $a); + } + + $a += [ + Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub( + [ + 'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY), + 'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED), + 'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN), + 'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS), + 'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS), + 'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS), + 'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS), + 'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS), + 'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS), + 'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER), + 'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE), + 'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE), + 'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), + 'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH), + 'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION), + 'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE), + 'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED), + 'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS), + 'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS), + 'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE), + ] + ), + Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub( + [ + 'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX), + 'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX), + 'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX), + 'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX), + 'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER), + 'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE), + 'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET), + 'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS), + ] + ), + Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub( + [ + 'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL), + 'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL), + 'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL), + 'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL), + 'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL), + 'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL), + 'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL), + 'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL), + 'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), + 'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL), + 'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL), + 'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL), + 'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL), + 'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL), + 'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL), + 'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL), + 'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL), + 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), + ] + ), + ]; + + return self::castError($c, $a); + } + + public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, $isNested) + { + $a += [ + Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(), + Caster::PREFIX_VIRTUAL.'id' => $c->getID(), + Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(), + ]; + + if ($c->useDaylightTime()) { + $a += [ + Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(), + ]; + } + + return self::castError($c, $a); + } + + public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $a += [ + Caster::PREFIX_VIRTUAL.'type' => $c->getType(), + Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(), + Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(), + Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(), + Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(), + Caster::PREFIX_VIRTUAL.'time' => $c->getTime(), + Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(), + Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(), + Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), + ]; + + return self::castError($c, $a); + } + + public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $a += [ + Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), + Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), + Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(), + Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(), + Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(), + Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(), + Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(), + Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), + ]; + + return self::castError($c, $a); + } + + private static function castError($c, array $a): array + { + if ($errorCode = $c->getErrorCode()) { + $a += [ + Caster::PREFIX_VIRTUAL.'error_code' => $errorCode, + Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(), + ]; + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/LinkStub.php b/vendor/symfony/var-dumper/Caster/LinkStub.php index f32f342..6360716 100644 --- a/vendor/symfony/var-dumper/Caster/LinkStub.php +++ b/vendor/symfony/var-dumper/Caster/LinkStub.php @@ -23,7 +23,7 @@ class LinkStub extends ConstStub private static $vendorRoots; private static $composerRoots; - public function __construct($label, $line = 0, $href = null) + public function __construct($label, int $line = 0, $href = null) { $this->value = $label; @@ -63,15 +63,15 @@ public function __construct($label, $line = 0, $href = null) } } - private function getComposerRoot($file, &$inVendor) + private function getComposerRoot(string $file, bool &$inVendor) { if (null === self::$vendorRoots) { - self::$vendorRoots = array(); + self::$vendorRoots = []; foreach (get_declared_classes() as $class) { if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) { $r = new \ReflectionClass($class); - $v = \dirname(\dirname($r->getFileName())); + $v = \dirname($r->getFileName(), 2); if (file_exists($v.'/composer/installed.json')) { self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; } diff --git a/vendor/symfony/var-dumper/Caster/MemcachedCaster.php b/vendor/symfony/var-dumper/Caster/MemcachedCaster.php new file mode 100644 index 0000000..942eecb --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/MemcachedCaster.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Jan Schädlich + * + * @final since Symfony 4.4 + */ +class MemcachedCaster +{ + private static $optionConstants; + private static $defaultOptions; + + public static function castMemcached(\Memcached $c, array $a, Stub $stub, $isNested) + { + $a += [ + Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(), + Caster::PREFIX_VIRTUAL.'options' => new EnumStub( + self::getNonDefaultOptions($c) + ), + ]; + + return $a; + } + + private static function getNonDefaultOptions(\Memcached $c): array + { + self::$defaultOptions = self::$defaultOptions ?? self::discoverDefaultOptions(); + self::$optionConstants = self::$optionConstants ?? self::getOptionConstants(); + + $nonDefaultOptions = []; + foreach (self::$optionConstants as $constantKey => $value) { + if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) { + $nonDefaultOptions[$constantKey] = $option; + } + } + + return $nonDefaultOptions; + } + + private static function discoverDefaultOptions(): array + { + $defaultMemcached = new \Memcached(); + $defaultMemcached->addServer('127.0.0.1', 11211); + + $defaultOptions = []; + self::$optionConstants = self::$optionConstants ?? self::getOptionConstants(); + + foreach (self::$optionConstants as $constantKey => $value) { + $defaultOptions[$constantKey] = $defaultMemcached->getOption($value); + } + + return $defaultOptions; + } + + private static function getOptionConstants(): array + { + $reflectedMemcached = new \ReflectionClass(\Memcached::class); + + $optionConstants = []; + foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { + if (0 === strpos($constantKey, 'OPT_')) { + $optionConstants[$constantKey] = $value; + } + } + + return $optionConstants; + } +} diff --git a/vendor/symfony/var-dumper/Caster/MongoCaster.php b/vendor/symfony/var-dumper/Caster/MongoCaster.php deleted file mode 100644 index 3b8fb33..0000000 --- a/vendor/symfony/var-dumper/Caster/MongoCaster.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); - -/** - * Casts classes from the MongoDb extension to array representation. - * - * @author Nicolas Grekas - * - * @deprecated since version 3.4, to be removed in 4.0. - */ -class MongoCaster -{ - public static function castCursor(\MongoCursorInterface $cursor, array $a, Stub $stub, $isNested) - { - if ($info = $cursor->info()) { - foreach ($info as $k => $v) { - $a[Caster::PREFIX_VIRTUAL.$k] = $v; - } - } - $a[Caster::PREFIX_VIRTUAL.'dead'] = $cursor->dead(); - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/PdoCaster.php b/vendor/symfony/var-dumper/Caster/PdoCaster.php index 83a001d..d30ab01 100644 --- a/vendor/symfony/var-dumper/Caster/PdoCaster.php +++ b/vendor/symfony/var-dumper/Caster/PdoCaster.php @@ -17,56 +17,58 @@ * Casts PDO related classes to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class PdoCaster { - private static $pdoAttributes = array( - 'CASE' => array( + private static $pdoAttributes = [ + 'CASE' => [ \PDO::CASE_LOWER => 'LOWER', \PDO::CASE_NATURAL => 'NATURAL', \PDO::CASE_UPPER => 'UPPER', - ), - 'ERRMODE' => array( + ], + 'ERRMODE' => [ \PDO::ERRMODE_SILENT => 'SILENT', \PDO::ERRMODE_WARNING => 'WARNING', \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', - ), + ], 'TIMEOUT', 'PREFETCH', 'AUTOCOMMIT', 'PERSISTENT', 'DRIVER_NAME', 'SERVER_INFO', - 'ORACLE_NULLS' => array( + 'ORACLE_NULLS' => [ \PDO::NULL_NATURAL => 'NATURAL', \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', \PDO::NULL_TO_STRING => 'TO_STRING', - ), + ], 'CLIENT_VERSION', 'SERVER_VERSION', 'STATEMENT_CLASS', 'EMULATE_PREPARES', 'CONNECTION_STATUS', 'STRINGIFY_FETCHES', - 'DEFAULT_FETCH_MODE' => array( + 'DEFAULT_FETCH_MODE' => [ \PDO::FETCH_ASSOC => 'ASSOC', \PDO::FETCH_BOTH => 'BOTH', \PDO::FETCH_LAZY => 'LAZY', \PDO::FETCH_NUM => 'NUM', \PDO::FETCH_OBJ => 'OBJ', - ), - ); + ], + ]; public static function castPdo(\PDO $c, array $a, Stub $stub, $isNested) { - $attr = array(); + $attr = []; $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); foreach (self::$pdoAttributes as $k => $v) { if (!isset($k[0])) { $k = $v; - $v = array(); + $v = []; } try { @@ -85,11 +87,11 @@ public static function castPdo(\PDO $c, array $a, Stub $stub, $isNested) } $prefix = Caster::PREFIX_VIRTUAL; - $a += array( + $a += [ $prefix.'inTransaction' => method_exists($c, 'inTransaction'), $prefix.'errorInfo' => $c->errorInfo(), $prefix.'attributes' => new EnumStub($attr), - ); + ]; if ($a[$prefix.'inTransaction']) { $a[$prefix.'inTransaction'] = $c->inTransaction(); diff --git a/vendor/symfony/var-dumper/Caster/PgSqlCaster.php b/vendor/symfony/var-dumper/Caster/PgSqlCaster.php index 88414e4..c54fb42 100644 --- a/vendor/symfony/var-dumper/Caster/PgSqlCaster.php +++ b/vendor/symfony/var-dumper/Caster/PgSqlCaster.php @@ -17,10 +17,12 @@ * Casts pqsql resources to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class PgSqlCaster { - private static $paramCodes = array( + private static $paramCodes = [ 'server_encoding', 'client_encoding', 'is_superuser', @@ -31,17 +33,17 @@ class PgSqlCaster 'integer_datetimes', 'application_name', 'standard_conforming_strings', - ); + ]; - private static $transactionStatus = array( + private static $transactionStatus = [ PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', - ); + ]; - private static $resultStatus = array( + private static $resultStatus = [ PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', @@ -50,9 +52,9 @@ class PgSqlCaster PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', - ); + ]; - private static $diagCodes = array( + private static $diagCodes = [ 'severity' => PGSQL_DIAG_SEVERITY, 'sqlstate' => PGSQL_DIAG_SQLSTATE, 'message' => PGSQL_DIAG_MESSAGE_PRIMARY, @@ -65,7 +67,7 @@ class PgSqlCaster 'file' => PGSQL_DIAG_SOURCE_FILE, 'line' => PGSQL_DIAG_SOURCE_LINE, 'function' => PGSQL_DIAG_SOURCE_FUNCTION, - ); + ]; public static function castLargeObject($lo, array $a, Stub $stub, $isNested) { @@ -127,14 +129,14 @@ public static function castResult($result, array $a, Stub $stub, $isNested) $fields = pg_num_fields($result); for ($i = 0; $i < $fields; ++$i) { - $field = array( + $field = [ 'name' => pg_field_name($result, $i), 'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), 'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), 'nullable' => (bool) pg_field_is_null($result, $i), 'storage' => pg_field_size($result, $i).' bytes', 'display' => pg_field_prtlen($result, $i).' chars', - ); + ]; if (' (OID: )' === $field['table']) { $field['table'] = null; } diff --git a/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php b/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php new file mode 100644 index 0000000..ec02f81 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use ProxyManager\Proxy\ProxyInterface; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Nicolas Grekas + * + * @final since Symfony 4.4 + */ +class ProxyManagerCaster +{ + public static function castProxy(ProxyInterface $c, array $a, Stub $stub, $isNested) + { + if ($parent = get_parent_class($c)) { + $stub->class .= ' - '.$parent; + } + $stub->class .= '@proxy'; + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/RedisCaster.php b/vendor/symfony/var-dumper/Caster/RedisCaster.php index 409a0d4..e92c65b 100644 --- a/vendor/symfony/var-dumper/Caster/RedisCaster.php +++ b/vendor/symfony/var-dumper/Caster/RedisCaster.php @@ -17,61 +17,136 @@ * Casts Redis class from ext-redis to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class RedisCaster { - private static $serializer = array( + private static $serializer = [ \Redis::SERIALIZER_NONE => 'NONE', \Redis::SERIALIZER_PHP => 'PHP', 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY - ); + ]; + + private static $mode = [ + \Redis::ATOMIC => 'ATOMIC', + \Redis::MULTI => 'MULTI', + \Redis::PIPELINE => 'PIPELINE', + ]; + + private static $compression = [ + 0 => 'NONE', // Redis::COMPRESSION_NONE + 1 => 'LZF', // Redis::COMPRESSION_LZF + ]; + + private static $failover = [ + \RedisCluster::FAILOVER_NONE => 'NONE', + \RedisCluster::FAILOVER_ERROR => 'ERROR', + \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE', + \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES', + ]; public static function castRedis(\Redis $c, array $a, Stub $stub, $isNested) { $prefix = Caster::PREFIX_VIRTUAL; - if (\defined('HHVM_VERSION_ID')) { - if (isset($a[Caster::PREFIX_PROTECTED.'serializer'])) { - $ser = $a[Caster::PREFIX_PROTECTED.'serializer']; - $a[Caster::PREFIX_PROTECTED.'serializer'] = isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser; - } - - return $a; - } - if (!$connected = $c->isConnected()) { - return $a + array( + return $a + [ $prefix.'isConnected' => $connected, - ); + ]; } - $ser = $c->getOption(\Redis::OPT_SERIALIZER); - $retry = \defined('Redis::OPT_SCAN') ? $c->getOption(\Redis::OPT_SCAN) : 0; + $mode = $c->getMode(); - return $a + array( + return $a + [ $prefix.'isConnected' => $connected, $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), $prefix.'auth' => $c->getAuth(), + $prefix.'mode' => isset(self::$mode[$mode]) ? new ConstStub(self::$mode[$mode], $mode) : $mode, $prefix.'dbNum' => $c->getDbNum(), $prefix.'timeout' => $c->getTimeout(), + $prefix.'lastError' => $c->getLastError(), $prefix.'persistentId' => $c->getPersistentID(), - $prefix.'options' => new EnumStub(array( - 'READ_TIMEOUT' => $c->getOption(\Redis::OPT_READ_TIMEOUT), - 'SERIALIZER' => isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser, - 'PREFIX' => $c->getOption(\Redis::OPT_PREFIX), - 'SCAN' => new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry), - )), - ); + $prefix.'options' => self::getRedisOptions($c), + ]; } public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, $isNested) { $prefix = Caster::PREFIX_VIRTUAL; - return $a + array( + return $a + [ $prefix.'hosts' => $c->_hosts(), $prefix.'function' => ClassStub::wrapCallable($c->_function()), - ); + $prefix.'lastError' => $c->getLastError(), + $prefix.'options' => self::getRedisOptions($c), + ]; + } + + public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER); + + $a += [ + $prefix.'_masters' => $c->_masters(), + $prefix.'_redir' => $c->_redir(), + $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()), + $prefix.'lastError' => $c->getLastError(), + $prefix.'options' => self::getRedisOptions($c, [ + 'SLAVE_FAILOVER' => isset(self::$failover[$failover]) ? new ConstStub(self::$failover[$failover], $failover) : $failover, + ]), + ]; + + return $a; + } + + /** + * @param \Redis|\RedisArray|\RedisCluster $redis + */ + private static function getRedisOptions($redis, array $options = []): EnumStub + { + $serializer = $redis->getOption(\Redis::OPT_SERIALIZER); + if (\is_array($serializer)) { + foreach ($serializer as &$v) { + if (isset(self::$serializer[$v])) { + $v = new ConstStub(self::$serializer[$v], $v); + } + } + } elseif (isset(self::$serializer[$serializer])) { + $serializer = new ConstStub(self::$serializer[$serializer], $serializer); + } + + $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0; + if (\is_array($compression)) { + foreach ($compression as &$v) { + if (isset(self::$compression[$v])) { + $v = new ConstStub(self::$compression[$v], $v); + } + } + } elseif (isset(self::$compression[$compression])) { + $compression = new ConstStub(self::$compression[$compression], $compression); + } + + $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0; + if (\is_array($retry)) { + foreach ($retry as &$v) { + $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v); + } + } else { + $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry); + } + + $options += [ + 'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : 0, + 'READ_TIMEOUT' => $redis->getOption(\Redis::OPT_READ_TIMEOUT), + 'COMPRESSION' => $compression, + 'SERIALIZER' => $serializer, + 'PREFIX' => $redis->getOption(\Redis::OPT_PREFIX), + 'SCAN' => $retry, + ]; + + return new EnumStub($options); } } diff --git a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php index 9bfef06..45bd5e6 100644 --- a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php +++ b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php @@ -17,10 +17,14 @@ * Casts Reflector related classes to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class ReflectionCaster { - private static $extraMap = array( + const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; + + private static $extraMap = [ 'docComment' => 'getDocComment', 'extension' => 'getExtensionName', 'isDisabled' => 'isDisabled', @@ -29,51 +33,53 @@ class ReflectionCaster 'isUserDefined' => 'isUserDefined', 'isGenerator' => 'isGenerator', 'isVariadic' => 'isVariadic', - ); + ]; public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested, $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; $c = new \ReflectionFunction($c); - $stub->class = 'Closure'; // HHVM generates unique class names for closures $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); if (false === strpos($c->name, '{closure}')) { $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; unset($a[$prefix.'class']); } + unset($a[$prefix.'extra']); - if (isset($a[$prefix.'parameters'])) { - foreach ($a[$prefix.'parameters']->value as &$v) { - $param = $v; - $v = new EnumStub(array()); - foreach (static::castParameter($param, array(), $stub, true) as $k => $param) { - if ("\0" === $k[0]) { - $v->value[substr($k, 3)] = $param; - } - } - unset($v->value['position'], $v->value['isVariadic'], $v->value['byReference'], $v); - } + $stub->class .= self::getSignature($a); + + if ($f = $c->getFileName()) { + $stub->attr['file'] = $f; + $stub->attr['line'] = $c->getStartLine(); + } + + unset($a[$prefix.'parameters']); + + if ($filter & Caster::EXCLUDE_VERBOSE) { + $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a); + + return []; } - if (!($filter & Caster::EXCLUDE_VERBOSE) && $f = $c->getFileName()) { + if ($f) { $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); } - $prefix = Caster::PREFIX_DYNAMIC; - unset($a['name'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']); + return $a; + } + + public static function unsetClosureFileInfo(\Closure $c, array $a) + { + unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']); return $a; } public static function castGenerator(\Generator $c, array $a, Stub $stub, $isNested) { - if (!class_exists('ReflectionGenerator', false)) { - return $a; - } - // Cannot create ReflectionGenerator based on a terminated Generator try { $reflectionGenerator = new \ReflectionGenerator($c); @@ -90,11 +96,11 @@ public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNes { $prefix = Caster::PREFIX_VIRTUAL; - $a += array( - $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : $c->__toString(), + $a += [ + $prefix.'name' => $c->getName(), $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), - ); + ]; return $a; } @@ -107,28 +113,26 @@ public static function castReflectionGenerator(\ReflectionGenerator $c, array $a $a[$prefix.'this'] = new CutStub($c->getThis()); } $function = $c->getFunction(); - $frame = array( + $frame = [ 'class' => isset($function->class) ? $function->class : null, 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, 'function' => $function->name, 'file' => $c->getExecutingFile(), 'line' => $c->getExecutingLine(), - ); + ]; if ($trace = $c->getTrace(DEBUG_BACKTRACE_IGNORE_ARGS)) { $function = new \ReflectionGenerator($c->getExecutingGenerator()); - array_unshift($trace, array( + array_unshift($trace, [ 'function' => 'yield', 'file' => $function->getExecutingFile(), 'line' => $function->getExecutingLine() - 1, - )); + ]); $trace[] = $frame; $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); } else { $function = new FrameStub($frame, false, true); - $function = ExceptionCaster::castFrameStub($function, array(), $function, true); - $a[$prefix.'executing'] = new EnumStub(array( - "\0~separator= \0".$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'], - )); + $function = ExceptionCaster::castFrameStub($function, [], $function, true); + $a[$prefix.'executing'] = $function[$prefix.'src']; } $a[Caster::PREFIX_VIRTUAL.'closed'] = false; @@ -144,11 +148,11 @@ public static function castClass(\ReflectionClass $c, array $a, Stub $stub, $isN $a[$prefix.'modifiers'] = implode(' ', $n); } - self::addMap($a, $c, array( + self::addMap($a, $c, [ 'extends' => 'getParentClass', 'implements' => 'getInterfaceNames', 'constants' => 'getConstants', - )); + ]); foreach ($c->getProperties() as $n) { $a[$prefix.'properties'][$n->name] = $n; @@ -169,17 +173,17 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra { $prefix = Caster::PREFIX_VIRTUAL; - self::addMap($a, $c, array( + self::addMap($a, $c, [ 'returnsReference' => 'returnsReference', 'returnType' => 'getReturnType', 'class' => 'getClosureScopeClass', 'this' => 'getClosureThis', - )); + ]); if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; - $v = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); - $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, array(class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '')); + $v = $v->getName(); + $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } if (isset($a[$prefix.'class'])) { $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); @@ -190,7 +194,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra foreach ($c->getParameters() as $v) { $k = '$'.$v->name; - if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + if ($v->isVariadic()) { $k = '...'.$k; } if ($v->isPassedByReference()) { @@ -202,7 +206,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); } - if ($v = $c->getStaticVariables()) { + if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) { foreach ($v as $k => &$v) { if (\is_object($v)) { $a[$prefix.'use']['$'.$k] = new CutStub($v); @@ -218,9 +222,6 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra self::addExtra($a, $c); } - // Added by HHVM - unset($a[Caster::PREFIX_DYNAMIC.'static']); - return $a; } @@ -235,44 +236,33 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st { $prefix = Caster::PREFIX_VIRTUAL; - // Added by HHVM - unset($a['info']); - - self::addMap($a, $c, array( + self::addMap($a, $c, [ 'position' => 'getPosition', 'isVariadic' => 'isVariadic', 'byReference' => 'isPassedByReference', 'allowsNull' => 'allowsNull', - )); + ]); - if (method_exists($c, 'getType')) { - if ($v = $c->getType()) { - $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); - } - } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) { - $a[$prefix.'typeHint'] = $v[1]; + if ($v = $c->getType()) { + $a[$prefix.'typeHint'] = $v->getName(); } if (isset($a[$prefix.'typeHint'])) { $v = $a[$prefix.'typeHint']; - $a[$prefix.'typeHint'] = new ClassStub($v, array(class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '')); + $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } else { unset($a[$prefix.'allowsNull']); } try { $a[$prefix.'default'] = $v = $c->getDefaultValue(); - if (method_exists($c, 'isDefaultValueConstant') && $c->isDefaultValueConstant()) { + if ($c->isDefaultValueConstant()) { $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); } if (null === $v) { unset($a[$prefix.'allowsNull']); } } catch (\ReflectionException $e) { - if (isset($a[$prefix.'typeHint']) && $c->allowsNull() && !class_exists('ReflectionNamedType', false)) { - $a[$prefix.'default'] = null; - unset($a[$prefix.'allowsNull']); - } } return $a; @@ -286,9 +276,16 @@ public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub return $a; } + public static function castReference(\ReflectionReference $c, array $a, Stub $stub, $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); + + return $a; + } + public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, $isNested) { - self::addMap($a, $c, array( + self::addMap($a, $c, [ 'version' => 'getVersion', 'dependencies' => 'getDependencies', 'iniEntries' => 'getIniEntries', @@ -297,26 +294,72 @@ public static function castExtension(\ReflectionExtension $c, array $a, Stub $st 'constants' => 'getConstants', 'functions' => 'getFunctions', 'classes' => 'getClasses', - )); + ]); return $a; } public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, $isNested) { - self::addMap($a, $c, array( + self::addMap($a, $c, [ 'version' => 'getVersion', 'author' => 'getAuthor', 'copyright' => 'getCopyright', 'url' => 'getURL', - )); + ]); return $a; } - private static function addExtra(&$a, \Reflector $c) + public static function getSignature(array $a) + { + $prefix = Caster::PREFIX_VIRTUAL; + $signature = ''; + + if (isset($a[$prefix.'parameters'])) { + foreach ($a[$prefix.'parameters']->value as $k => $param) { + $signature .= ', '; + if ($type = $param->getType()) { + if (!$param->isOptional() && $param->allowsNull()) { + $signature .= '?'; + } + $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; + } + $signature .= $k; + + if (!$param->isDefaultValueAvailable()) { + continue; + } + $v = $param->getDefaultValue(); + $signature .= ' = '; + + if ($param->isDefaultValueConstant()) { + $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1); + } elseif (null === $v) { + $signature .= 'null'; + } elseif (\is_array($v)) { + $signature .= $v ? '[…'.\count($v).']' : '[]'; + } elseif (\is_string($v)) { + $signature .= 10 > \strlen($v) && false === strpos($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; + } elseif (\is_bool($v)) { + $signature .= $v ? 'true' : 'false'; + } else { + $signature .= $v; + } + } + } + $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')'; + + if (isset($a[$prefix.'returnType'])) { + $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1); + } + + return $signature; + } + + private static function addExtra(array &$a, \Reflector $c) { - $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : array(); + $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { $x['file'] = new LinkStub($m, $c->getStartLine()); @@ -330,7 +373,7 @@ private static function addExtra(&$a, \Reflector $c) } } - private static function addMap(&$a, \Reflector $c, $map, $prefix = Caster::PREFIX_VIRTUAL) + private static function addMap(array &$a, \Reflector $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL) { foreach ($map as $k => $m) { if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { diff --git a/vendor/symfony/var-dumper/Caster/ResourceCaster.php b/vendor/symfony/var-dumper/Caster/ResourceCaster.php index 3cdb27c..78b5aab 100644 --- a/vendor/symfony/var-dumper/Caster/ResourceCaster.php +++ b/vendor/symfony/var-dumper/Caster/ResourceCaster.php @@ -17,6 +17,8 @@ * Casts common resource types to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class ResourceCaster { @@ -69,4 +71,30 @@ public static function castMysqlLink($h, array $a, Stub $stub, $isNested) return $a; } + + public static function castOpensslX509($h, array $a, Stub $stub, $isNested) + { + $stub->cut = -1; + $info = openssl_x509_parse($h, false); + + $pin = openssl_pkey_get_public($h); + $pin = openssl_pkey_get_details($pin)['key']; + $pin = \array_slice(explode("\n", $pin), 1, -2); + $pin = base64_decode(implode('', $pin)); + $pin = base64_encode(hash('sha256', $pin, true)); + + $a += [ + 'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])), + 'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])), + 'expiry' => new ConstStub(date(\DateTime::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']), + 'fingerprint' => new EnumStub([ + 'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)), + 'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)), + 'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)), + 'pin-sha256' => new ConstStub($pin), + ]), + ]; + + return $a; + } } diff --git a/vendor/symfony/var-dumper/Caster/SplCaster.php b/vendor/symfony/var-dumper/Caster/SplCaster.php index 0780c0a..b2d4e3c 100644 --- a/vendor/symfony/var-dumper/Caster/SplCaster.php +++ b/vendor/symfony/var-dumper/Caster/SplCaster.php @@ -17,15 +17,17 @@ * Casts SPL related classes to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class SplCaster { - private static $splFileObjectFlags = array( + private static $splFileObjectFlags = [ \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', \SplFileObject::READ_AHEAD => 'READ_AHEAD', \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', \SplFileObject::READ_CSV => 'READ_CSV', - ); + ]; public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested) { @@ -39,9 +41,9 @@ public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), - ); + ]; return $a; } @@ -52,10 +54,10 @@ public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, S $mode = $c->getIteratorMode(); $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); - $a += array( + $a += [ $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), $prefix.'dllist' => iterator_to_array($c), - ); + ]; $c->setIteratorMode($mode); return $a; @@ -63,7 +65,7 @@ public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, S public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNested) { - static $map = array( + static $map = [ 'path' => 'getPath', 'filename' => 'getFilename', 'basename' => 'getBasename', @@ -86,10 +88,16 @@ public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNe 'dir' => 'isDir', 'link' => 'isLink', 'linkTarget' => 'getLinkTarget', - ); + ]; $prefix = Caster::PREFIX_VIRTUAL; + if (false === $c->getPathname()) { + $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; + + return $a; + } + foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); @@ -105,7 +113,7 @@ public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNe $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); } - static $mapDate = array('aTime', 'mTime', 'cTime'); + static $mapDate = ['aTime', 'mTime', 'cTime']; foreach ($mapDate as $key) { if (isset($a[$prefix.$key])) { $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); @@ -117,14 +125,14 @@ public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNe public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $isNested) { - static $map = array( + static $map = [ 'csvControl' => 'getCsvControl', 'flags' => 'getFlags', 'maxLineLen' => 'getMaxLineLen', 'fstat' => 'fstat', 'eof' => 'eof', 'key' => 'key', - ); + ]; $prefix = Caster::PREFIX_VIRTUAL; @@ -136,7 +144,7 @@ public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $ } if (isset($a[$prefix.'flags'])) { - $flagsArray = array(); + $flagsArray = []; foreach (self::$splFileObjectFlags as $value => $name) { if ($a[$prefix.'flags'] & $value) { $flagsArray[] = $name; @@ -146,7 +154,7 @@ public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $ } if (isset($a[$prefix.'fstat'])) { - $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], array('dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks')); + $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); } return $a; @@ -154,29 +162,29 @@ public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $ public static function castFixedArray(\SplFixedArray $c, array $a, Stub $stub, $isNested) { - $a += array( + $a += [ Caster::PREFIX_VIRTUAL.'storage' => $c->toArray(), - ); + ]; return $a; } public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, $isNested) { - $storage = array(); + $storage = []; unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 $clone = clone $c; foreach ($clone as $obj) { - $storage[] = array( + $storage[] = [ 'object' => $obj, 'info' => $clone->getInfo(), - ); + ]; } - $a += array( + $a += [ Caster::PREFIX_VIRTUAL.'storage' => $storage, - ); + ]; return $a; } @@ -188,7 +196,14 @@ public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub return $a; } - private static function castSplArray($c, array $a, Stub $stub, $isNested) + public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); + + return $a; + } + + private static function castSplArray($c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $class = $stub->class; @@ -199,10 +214,10 @@ private static function castSplArray($c, array $a, Stub $stub, $isNested) $a = Caster::castObject($c, $class); $c->setFlags($flags); } - $a += array( + $a += [ $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), - ); + ]; if ($c instanceof \ArrayObject) { $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); } diff --git a/vendor/symfony/var-dumper/Caster/StubCaster.php b/vendor/symfony/var-dumper/Caster/StubCaster.php index b0bf5dc..b6332fb 100644 --- a/vendor/symfony/var-dumper/Caster/StubCaster.php +++ b/vendor/symfony/var-dumper/Caster/StubCaster.php @@ -17,6 +17,8 @@ * Casts a caster's Stub. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class StubCaster { @@ -35,7 +37,7 @@ public static function castStub(Stub $c, array $a, Stub $stub, $isNested) $stub->class = Stub::STRING_BINARY; } - $a = array(); + $a = []; } return $a; @@ -51,7 +53,7 @@ public static function cutInternals($obj, array $a, Stub $stub, $isNested) if ($isNested) { $stub->cut += \count($a); - return array(); + return []; } return $a; @@ -66,7 +68,7 @@ public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested) $stub->cut = $c->cut; $stub->attr = $c->attr; - $a = array(); + $a = []; if ($c->value) { foreach (array_keys($c->value) as $k) { diff --git a/vendor/symfony/var-dumper/Caster/SymfonyCaster.php b/vendor/symfony/var-dumper/Caster/SymfonyCaster.php index 5d9decb..ad7bb71 100644 --- a/vendor/symfony/var-dumper/Caster/SymfonyCaster.php +++ b/vendor/symfony/var-dumper/Caster/SymfonyCaster.php @@ -14,23 +14,27 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\VarDumper\Cloner\Stub; +/** + * @final since Symfony 4.4 + */ class SymfonyCaster { - private static $requestGetters = array( + private static $requestGetters = [ 'pathInfo' => 'getPathInfo', 'requestUri' => 'getRequestUri', 'baseUrl' => 'getBaseUrl', 'basePath' => 'getBasePath', 'method' => 'getMethod', 'format' => 'getRequestFormat', - ); + ]; public static function castRequest(Request $request, array $a, Stub $stub, $isNested) { $clone = null; foreach (self::$requestGetters as $prop => $getter) { - if (null === $a[Caster::PREFIX_PROTECTED.$prop]) { + $key = Caster::PREFIX_PROTECTED.$prop; + if (\array_key_exists($key, $a) && null === $a[$key]) { if (null === $clone) { $clone = clone $request; } @@ -40,4 +44,26 @@ public static function castRequest(Request $request, array $a, Stub $stub, $isNe return $a; } + + public static function castHttpClient($client, array $a, Stub $stub, $isNested) + { + $multiKey = sprintf("\0%s\0multi", \get_class($client)); + if (isset($a[$multiKey])) { + $a[$multiKey] = new CutStub($a[$multiKey]); + } + + return $a; + } + + public static function castHttpClientResponse($response, array $a, Stub $stub, $isNested) + { + $stub->cut += \count($a); + $a = []; + + foreach ($response->getInfo() as $k => $v) { + $a[Caster::PREFIX_VIRTUAL.$k] = $v; + } + + return $a; + } } diff --git a/vendor/symfony/var-dumper/Caster/TraceStub.php b/vendor/symfony/var-dumper/Caster/TraceStub.php index 59548ac..5eea1c8 100644 --- a/vendor/symfony/var-dumper/Caster/TraceStub.php +++ b/vendor/symfony/var-dumper/Caster/TraceStub.php @@ -25,7 +25,7 @@ class TraceStub extends Stub public $sliceLength; public $numberingOffset; - public function __construct(array $trace, $keepArgs = true, $sliceOffset = 0, $sliceLength = null, $numberingOffset = 0) + public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0) { $this->value = $trace; $this->keepArgs = $keepArgs; diff --git a/vendor/symfony/var-dumper/Caster/UuidCaster.php b/vendor/symfony/var-dumper/Caster/UuidCaster.php new file mode 100644 index 0000000..b102774 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/UuidCaster.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Ramsey\Uuid\UuidInterface; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Grégoire Pineau + */ +final class UuidCaster +{ + public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array + { + $a += [ + Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, + ]; + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php index 19b26e6..d18e474 100644 --- a/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php +++ b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php @@ -16,10 +16,12 @@ * Casts XmlReader class to array representation. * * @author Baptiste Clavié + * + * @final since Symfony 4.4 */ class XmlReaderCaster { - private static $nodeTypes = array( + private static $nodeTypes = [ \XMLReader::NONE => 'NONE', \XMLReader::ELEMENT => 'ELEMENT', \XMLReader::ATTRIBUTE => 'ATTRIBUTE', @@ -38,12 +40,12 @@ class XmlReaderCaster \XMLReader::END_ELEMENT => 'END_ELEMENT', \XMLReader::END_ENTITY => 'END_ENTITY', \XMLReader::XML_DECLARATION => 'XML_DECLARATION', - ); + ]; public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, $isNested) { $props = Caster::PREFIX_VIRTUAL.'parserProperties'; - $info = array( + $info = [ 'localName' => $reader->localName, 'prefix' => $reader->prefix, 'nodeType' => new ConstStub(self::$nodeTypes[$reader->nodeType], $reader->nodeType), @@ -55,20 +57,20 @@ public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, $ 'value' => $reader->value, 'namespaceURI' => $reader->namespaceURI, 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, - $props => array( + $props => [ 'LOADDTD' => $reader->getParserProperty(\XMLReader::LOADDTD), 'DEFAULTATTRS' => $reader->getParserProperty(\XMLReader::DEFAULTATTRS), 'VALIDATE' => $reader->getParserProperty(\XMLReader::VALIDATE), 'SUBST_ENTITIES' => $reader->getParserProperty(\XMLReader::SUBST_ENTITIES), - ), - ); + ], + ]; - if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, array(), $count)) { + if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { $info[$props] = new EnumStub($info[$props]); $info[$props]->cut = $count; } - $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, array(), $count); + $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); // +2 because hasValue and hasAttributes are always filtered $stub->cut += $count + 2; diff --git a/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php b/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php index 5d0207e..ce0317c 100644 --- a/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php +++ b/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php @@ -17,10 +17,12 @@ * Casts XML resources to array representation. * * @author Nicolas Grekas + * + * @final since Symfony 4.4 */ class XmlResourceCaster { - private static $xmlErrors = array( + private static $xmlErrors = [ XML_ERROR_NONE => 'XML_ERROR_NONE', XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', @@ -43,7 +45,7 @@ class XmlResourceCaster XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', - ); + ]; public static function castXml($h, array $a, Stub $stub, $isNested) { diff --git a/vendor/symfony/var-dumper/Cloner/AbstractCloner.php b/vendor/symfony/var-dumper/Cloner/AbstractCloner.php index 5501fc6..4bf22af 100644 --- a/vendor/symfony/var-dumper/Cloner/AbstractCloner.php +++ b/vendor/symfony/var-dumper/Cloner/AbstractCloner.php @@ -21,124 +21,154 @@ */ abstract class AbstractCloner implements ClonerInterface { - public static $defaultCasters = array( - '__PHP_Incomplete_Class' => array('Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'), - - 'Symfony\Component\VarDumper\Caster\CutStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'), - 'Symfony\Component\VarDumper\Caster\CutArrayStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'), - 'Symfony\Component\VarDumper\Caster\ConstStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'), - 'Symfony\Component\VarDumper\Caster\EnumStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'), - - 'Closure' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'), - 'Generator' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'), - 'ReflectionType' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'), - 'ReflectionGenerator' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'), - 'ReflectionClass' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'), - 'ReflectionFunctionAbstract' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'), - 'ReflectionMethod' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'), - 'ReflectionParameter' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'), - 'ReflectionProperty' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'), - 'ReflectionExtension' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'), - 'ReflectionZendExtension' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'), - - 'Doctrine\Common\Persistence\ObjectManager' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'), - 'Doctrine\Common\Proxy\Proxy' => array('Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'), - 'Doctrine\ORM\Proxy\Proxy' => array('Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'), - 'Doctrine\ORM\PersistentCollection' => array('Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'), - - 'DOMException' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'), - 'DOMStringList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'), - 'DOMNameList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'), - 'DOMImplementation' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'), - 'DOMImplementationList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'), - 'DOMNode' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'), - 'DOMNameSpaceNode' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'), - 'DOMDocument' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'), - 'DOMNodeList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'), - 'DOMNamedNodeMap' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'), - 'DOMCharacterData' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'), - 'DOMAttr' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'), - 'DOMElement' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'), - 'DOMText' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'), - 'DOMTypeinfo' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'), - 'DOMDomError' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'), - 'DOMLocator' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'), - 'DOMDocumentType' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'), - 'DOMNotation' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'), - 'DOMEntity' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'), - 'DOMProcessingInstruction' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'), - 'DOMXPath' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'), - - 'XmlReader' => array('Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'), - - 'ErrorException' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'), - 'Exception' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'), - 'Error' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'), - 'Symfony\Component\DependencyInjection\ContainerInterface' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'), - 'Symfony\Component\HttpFoundation\Request' => array('Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'), - 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'), - 'Symfony\Component\VarDumper\Caster\TraceStub' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'), - 'Symfony\Component\VarDumper\Caster\FrameStub' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'), - 'Symfony\Component\Debug\Exception\SilencedErrorContext' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'), - - 'PHPUnit_Framework_MockObject_MockObject' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'), - 'Prophecy\Prophecy\ProphecySubjectInterface' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'), - 'Mockery\MockInterface' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'), - - 'PDO' => array('Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'), - 'PDOStatement' => array('Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'), - - 'AMQPConnection' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'), - 'AMQPChannel' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'), - 'AMQPQueue' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'), - 'AMQPExchange' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'), - 'AMQPEnvelope' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'), - - 'ArrayObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'), - 'ArrayIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'), - 'SplDoublyLinkedList' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'), - 'SplFileInfo' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'), - 'SplFileObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'), - 'SplFixedArray' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFixedArray'), - 'SplHeap' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'), - 'SplObjectStorage' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'), - 'SplPriorityQueue' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'), - 'OuterIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'), - - 'MongoCursorInterface' => array('Symfony\Component\VarDumper\Caster\MongoCaster', 'castCursor'), - - 'Redis' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'), - 'RedisArray' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'), - - 'DateTimeInterface' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'), - 'DateInterval' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'), - 'DateTimeZone' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'), - 'DatePeriod' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'), - - ':curl' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'), - ':dba' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'), - ':dba persistent' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'), - ':gd' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'), - ':mysql link' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'), - ':pgsql large object' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'), - ':pgsql link' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'), - ':pgsql link persistent' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'), - ':pgsql result' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'), - ':process' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'), - ':stream' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'), - ':persistent stream' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'), - ':stream-context' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'), - ':xml' => array('Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'), - ); + public static $defaultCasters = [ + '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'], + + 'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], + 'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'], + 'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], + 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], + + 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], + 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], + 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], + 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], + 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], + 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], + 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], + 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], + 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], + 'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'], + 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], + 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], + + 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], + 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], + 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], + 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + + 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], + 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], + 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], + 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'], + 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], + 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], + 'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], + 'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], + 'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], + 'DOMTypeinfo' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'], + 'DOMDomError' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'], + 'DOMLocator' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'], + 'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], + 'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], + 'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], + 'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], + 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'], + + 'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], + + 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], + 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], + 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], + 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], + 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], + 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], + 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], + 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], + 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], + 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], + 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], + 'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], + + 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], + + 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], + + 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], + 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + + 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], + 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], + + 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], + 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], + 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], + 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], + 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], + + 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], + 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], + 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], + 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], + 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], + 'SplFixedArray' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFixedArray'], + 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], + 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], + 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], + 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], + 'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'], + + 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], + 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], + 'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'], + + 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], + 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], + 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], + 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], + + 'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'], + + 'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'], + 'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'], + 'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'], + 'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'], + 'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'], + + 'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'], + + 'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'], + 'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'], + 'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'], + 'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'], + + ':curl' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], + ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], + ':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'], + ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], + ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], + ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], + ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], + ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], + ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], + ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], + ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], + ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], + ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], + ]; protected $maxItems = 2500; protected $maxString = -1; protected $minDepth = 1; - protected $useExt; - private $casters = array(); + private $casters = []; private $prevErrorHandler; - private $classInfo = array(); + private $classInfo = []; private $filter = 0; /** @@ -152,7 +182,6 @@ public function __construct(array $casters = null) $casters = static::$defaultCasters; } $this->addCasters($casters); - $this->useExt = \extension_loaded('symfony_debug'); } /** @@ -168,7 +197,7 @@ public function __construct(array $casters = null) public function addCasters(array $casters) { foreach ($casters as $type => $callback) { - $this->casters[strtolower($type)][] = \is_string($callback) && false !== strpos($callback, '::') ? explode('::', $callback, 2) : $callback; + $this->casters[$type][] = $callback; } } @@ -213,14 +242,14 @@ public function setMinDepth($minDepth) */ public function cloneVar($var, $filter = 0) { - $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) { + $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) { // Cloner never dies throw new \ErrorException($msg, 0, $type, $file, $line); } if ($this->prevErrorHandler) { - return \call_user_func($this->prevErrorHandler, $type, $msg, $file, $line, $context); + return ($this->prevErrorHandler)($type, $msg, $file, $line, $context); } return false; @@ -253,7 +282,6 @@ abstract protected function doClone($var); /** * Casts an object to an array representation. * - * @param Stub $stub The Stub for the casted object * @param bool $isNested True if the object is nested in the dumped structure * * @return array The object casted as array @@ -267,25 +295,32 @@ protected function castObject(Stub $stub, $isNested) $stub->class = get_parent_class($class).'@anonymous'; } if (isset($this->classInfo[$class])) { - list($i, $parents, $hasDebugInfo) = $this->classInfo[$class]; + list($i, $parents, $hasDebugInfo, $fileInfo) = $this->classInfo[$class]; } else { $i = 2; - $parents = array(strtolower($class)); + $parents = [$class]; $hasDebugInfo = method_exists($class, '__debugInfo'); foreach (class_parents($class) as $p) { - $parents[] = strtolower($p); + $parents[] = $p; ++$i; } foreach (class_implements($class) as $p) { - $parents[] = strtolower($p); + $parents[] = $p; ++$i; } $parents[] = '*'; - $this->classInfo[$class] = array($i, $parents, $hasDebugInfo); + $r = new \ReflectionClass($class); + $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [ + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ]; + + $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo]; } + $stub->attr += $fileInfo; $a = Caster::castObject($obj, $class, $hasDebugInfo); try { @@ -297,7 +332,7 @@ protected function castObject(Stub $stub, $isNested) } } } catch (\Exception $e) { - $a = array((Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)) + $a; + $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; @@ -306,14 +341,13 @@ protected function castObject(Stub $stub, $isNested) /** * Casts a resource to an array representation. * - * @param Stub $stub The Stub for the casted resource * @param bool $isNested True if the object is nested in the dumped structure * * @return array The resource casted as array */ protected function castResource(Stub $stub, $isNested) { - $a = array(); + $a = []; $res = $stub->value; $type = $stub->class; @@ -324,7 +358,7 @@ protected function castResource(Stub $stub, $isNested) } } } catch (\Exception $e) { - $a = array((Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)) + $a; + $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; diff --git a/vendor/symfony/var-dumper/Cloner/Cursor.php b/vendor/symfony/var-dumper/Cloner/Cursor.php index 888f4f2..5b0542f 100644 --- a/vendor/symfony/var-dumper/Cloner/Cursor.php +++ b/vendor/symfony/var-dumper/Cloner/Cursor.php @@ -38,6 +38,6 @@ class Cursor public $hashLength = 0; public $hashCut = 0; public $stop = false; - public $attr = array(); + public $attr = []; public $skipChildren = false; } diff --git a/vendor/symfony/var-dumper/Cloner/Data.php b/vendor/symfony/var-dumper/Cloner/Data.php index 8e4700d..7e148bf 100644 --- a/vendor/symfony/var-dumper/Cloner/Data.php +++ b/vendor/symfony/var-dumper/Cloner/Data.php @@ -12,6 +12,7 @@ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; /** * @author Nicolas Grekas @@ -24,6 +25,7 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate private $maxDepth = 20; private $maxItemsPerDepth = -1; private $useRefHandles = -1; + private $context = []; /** * @param array $data An array as returned by ClonerInterface::cloneVar() @@ -34,7 +36,7 @@ public function __construct(array $data) } /** - * @return string The type of the value + * @return string|null The type of the value */ public function getType() { @@ -58,10 +60,12 @@ public function getType() if (Stub::TYPE_RESOURCE === $item->type) { return $item->class.' resource'; } + + return null; } /** - * @param bool $recursive Whether values should be resolved recursively or not + * @param array|bool $recursive Whether values should be resolved recursively or not * * @return string|int|float|bool|array|Data[]|null A native representation of the original value */ @@ -79,7 +83,7 @@ public function getValue($recursive = false) return $item->value; } - $children = $item->position ? $this->data[$item->position] : array(); + $children = $item->position ? $this->data[$item->position] : []; foreach ($children as $k => $v) { if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { @@ -104,20 +108,24 @@ public function getValue($recursive = false) return $children; } + /** + * @return int + */ public function count() { return \count($this->getValue()); } + /** + * @return \Traversable + */ public function getIterator() { if (!\is_array($value = $this->getValue())) { throw new \LogicException(sprintf('%s object holds non-iterable type "%s".', self::class, \gettype($value))); } - foreach ($value as $k => $v) { - yield $k => $v; - } + yield from $value; } public function __get($key) @@ -125,15 +133,23 @@ public function __get($key) if (null !== $data = $this->seek($key)) { $item = $this->getStub($data->data[$data->position][$data->key]); - return $item instanceof Stub || array() === $item ? $data : $item; + return $item instanceof Stub || [] === $item ? $data : $item; } + + return null; } + /** + * @return bool + */ public function __isset($key) { return null !== $this->seek($key); } + /** + * @return bool + */ public function offsetExists($key) { return $this->__isset($key); @@ -154,6 +170,9 @@ public function offsetUnset($key) throw new \BadMethodCallException(self::class.' objects are immutable.'); } + /** + * @return string + */ public function __toString() { $value = $this->getValue(); @@ -165,24 +184,12 @@ public function __toString() return sprintf('%s (count=%d)', $this->getType(), \count($value)); } - /** - * @return array The raw data structure - * - * @deprecated since version 3.3. Use array or object access instead. - */ - public function getRawData() - { - @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the array or object access instead.', __METHOD__)); - - return $this->data; - } - /** * Returns a depth limited clone of $this. * * @param int $maxDepth The max dumped depth level * - * @return self A clone of $this + * @return static */ public function withMaxDepth($maxDepth) { @@ -197,7 +204,7 @@ public function withMaxDepth($maxDepth) * * @param int $maxItemsPerDepth The max number of items dumped per depth level * - * @return self A clone of $this + * @return static */ public function withMaxItemsPerDepth($maxItemsPerDepth) { @@ -212,7 +219,7 @@ public function withMaxItemsPerDepth($maxItemsPerDepth) * * @param bool $useRefHandles False to hide global ref. handles * - * @return self A clone of $this + * @return static */ public function withRefHandles($useRefHandles) { @@ -222,12 +229,23 @@ public function withRefHandles($useRefHandles) return $data; } + /** + * @return static + */ + public function withContext(array $context) + { + $data = clone $this; + $data->context = $context; + + return $data; + } + /** * Seeks to a specific key in nested data structures. * * @param string|int $key The key to seek to * - * @return self|null A clone of $this or null if the key is not set + * @return static|null Null if the key is not set */ public function seek($key) { @@ -237,9 +255,9 @@ public function seek($key) $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { - return; + return null; } - $keys = array($key); + $keys = [$key]; switch ($item->type) { case Stub::TYPE_OBJECT: @@ -252,14 +270,14 @@ public function seek($key) case Stub::TYPE_RESOURCE: break; default: - return; + return null; } $data = null; $children = $this->data[$item->position]; foreach ($keys as $key) { - if (isset($children[$key]) || array_key_exists($key, $children)) { + if (isset($children[$key]) || \array_key_exists($key, $children)) { $data = clone $this; $data->key = $key; $data->position = $item->position; @@ -275,19 +293,27 @@ public function seek($key) */ public function dump(DumperInterface $dumper) { - $refs = array(0); - $this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]); + $refs = [0]; + $cursor = new Cursor(); + + if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) { + $cursor->attr['if_links'] = true; + $cursor->hashType = -1; + $dumper->dumpScalar($cursor, 'default', '^'); + $cursor->attr = ['if_links' => true]; + $dumper->dumpScalar($cursor, 'default', ' '); + $cursor->hashType = 0; + } + + $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); } /** * Depth-first dumping of items. * - * @param DumperInterface $dumper The dumper being used for dumping - * @param Cursor $cursor A cursor used for tracking dumper state position - * @param array &$refs A map of all references discovered while dumping - * @param mixed $item A Stub object or the original value being dumped + * @param mixed $item A Stub object or the original value being dumped */ - private function dumpItem($dumper, $cursor, &$refs, $item) + private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, $item) { $cursor->refIndex = 0; $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; @@ -295,7 +321,7 @@ private function dumpItem($dumper, $cursor, &$refs, $item) $firstSeen = true; if (!$item instanceof Stub) { - $cursor->attr = array(); + $cursor->attr = []; $type = \gettype($item); if ($item && 'array' === $type) { $item = $this->getStub($item); @@ -336,10 +362,10 @@ private function dumpItem($dumper, $cursor, &$refs, $item) if ($cut >= 0) { $cut += \count($children); } - $children = array(); + $children = []; } } else { - $children = array(); + $children = []; } switch ($item->type) { case Stub::TYPE_STRING: @@ -385,17 +411,9 @@ private function dumpItem($dumper, $cursor, &$refs, $item) /** * Dumps children of hash structures. * - * @param DumperInterface $dumper - * @param Cursor $parentCursor The cursor of the parent hash - * @param array &$refs A map of all references discovered while dumping - * @param array $children The children to dump - * @param int $hashCut The number of items removed from the original hash - * @param string $hashType A Cursor::HASH_* const - * @param bool $dumpKeys Whether keys should be dumped or not - * * @return int The final number of removed items */ - private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType, $dumpKeys) + private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int { $cursor = clone $parentCursor; ++$cursor->depth; diff --git a/vendor/symfony/var-dumper/Cloner/DumperInterface.php b/vendor/symfony/var-dumper/Cloner/DumperInterface.php index cb498ff..ec8ef27 100644 --- a/vendor/symfony/var-dumper/Cloner/DumperInterface.php +++ b/vendor/symfony/var-dumper/Cloner/DumperInterface.php @@ -21,40 +21,36 @@ interface DumperInterface /** * Dumps a scalar value. * - * @param Cursor $cursor The Cursor position in the dump - * @param string $type The PHP type of the value being dumped - * @param string|int|float|bool $value The scalar value being dumped + * @param string $type The PHP type of the value being dumped + * @param string|int|float|bool $value The scalar value being dumped */ public function dumpScalar(Cursor $cursor, $type, $value); /** * Dumps a string. * - * @param Cursor $cursor The Cursor position in the dump - * @param string $str The string being dumped - * @param bool $bin Whether $str is UTF-8 or binary encoded - * @param int $cut The number of characters $str has been cut by + * @param string $str The string being dumped + * @param bool $bin Whether $str is UTF-8 or binary encoded + * @param int $cut The number of characters $str has been cut by */ public function dumpString(Cursor $cursor, $str, $bin, $cut); /** * Dumps while entering an hash. * - * @param Cursor $cursor The Cursor position in the dump - * @param int $type A Cursor::HASH_* const for the type of hash - * @param string $class The object class, resource type or array count - * @param bool $hasChild When the dump of the hash has child item + * @param int $type A Cursor::HASH_* const for the type of hash + * @param string|int $class The object class, resource type or array count + * @param bool $hasChild When the dump of the hash has child item */ public function enterHash(Cursor $cursor, $type, $class, $hasChild); /** * Dumps while leaving an hash. * - * @param Cursor $cursor The Cursor position in the dump - * @param int $type A Cursor::HASH_* const for the type of hash - * @param string $class The object class, resource type or array count - * @param bool $hasChild When the dump of the hash has child item - * @param int $cut The number of items the hash has been cut by + * @param int $type A Cursor::HASH_* const for the type of hash + * @param string|int $class The object class, resource type or array count + * @param bool $hasChild When the dump of the hash has child item + * @param int $cut The number of items the hash has been cut by */ public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut); } diff --git a/vendor/symfony/var-dumper/Cloner/Stub.php b/vendor/symfony/var-dumper/Cloner/Stub.php index 3b8e60d..7f6d05d 100644 --- a/vendor/symfony/var-dumper/Cloner/Stub.php +++ b/vendor/symfony/var-dumper/Cloner/Stub.php @@ -16,7 +16,7 @@ * * @author Nicolas Grekas */ -class Stub implements \Serializable +class Stub { const TYPE_REF = 1; const TYPE_STRING = 2; @@ -37,21 +37,31 @@ class Stub implements \Serializable public $handle = 0; public $refCount = 0; public $position = 0; - public $attr = array(); + public $attr = []; - /** - * @internal - */ - public function serialize() - { - return \serialize(array($this->class, $this->position, $this->cut, $this->type, $this->value, $this->handle, $this->refCount, $this->attr)); - } + private static $defaultProperties = []; /** * @internal */ - public function unserialize($serialized) + public function __sleep(): array { - list($this->class, $this->position, $this->cut, $this->type, $this->value, $this->handle, $this->refCount, $this->attr) = \unserialize($serialized); + $properties = []; + + if (!isset(self::$defaultProperties[$c = static::class])) { + self::$defaultProperties[$c] = get_class_vars($c); + + foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) { + unset(self::$defaultProperties[$c][$k]); + } + } + + foreach (self::$defaultProperties[$c] as $k => $v) { + if ($this->$k !== $v) { + $properties[] = $k; + } + } + + return $properties; } } diff --git a/vendor/symfony/var-dumper/Cloner/VarCloner.php b/vendor/symfony/var-dumper/Cloner/VarCloner.php index 5a67515..f640338 100644 --- a/vendor/symfony/var-dumper/Cloner/VarCloner.php +++ b/vendor/symfony/var-dumper/Cloner/VarCloner.php @@ -17,9 +17,7 @@ class VarCloner extends AbstractCloner { private static $gid; - private static $hashMask = 0; - private static $hashOffset = 0; - private static $arrayCache = array(); + private static $arrayCache = []; /** * {@inheritdoc} @@ -29,30 +27,27 @@ protected function doClone($var) $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the minimum depth $refsCounter = 0; // Hard references counter - $queue = array(array($var)); // This breadth-first queue is the return value - $indexedArrays = array(); // Map of queue indexes that hold numerically indexed arrays - $hardRefs = array(); // Map of original zval hashes to stub objects - $objRefs = array(); // Map of original object handles to their stub object counterpart - $resRefs = array(); // Map of original resource handles to their stub object counterpart - $values = array(); // Map of stub objects' hashes to original values + $queue = [[$var]]; // This breadth-first queue is the return value + $indexedArrays = []; // Map of queue indexes that hold numerically indexed arrays + $hardRefs = []; // Map of original zval ids to stub objects + $objRefs = []; // Map of original object handles to their stub object counterpart + $objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning + $resRefs = []; // Map of original resource handles to their stub object counterpart + $values = []; // Map of stub objects' ids to original values $maxItems = $this->maxItems; $maxString = $this->maxString; $minDepth = $this->minDepth; $currentDepth = 0; // Current tree depth $currentDepthFinalIndex = 0; // Final $queue index for current tree depth $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached - $cookie = (object) array(); // Unique object used to detect hard references + $cookie = (object) []; // Unique object used to detect hard references $a = null; // Array cast for nested structures $stub = null; // Stub capturing the main properties of an original item value // or null if the original value is used directly - if (!self::$hashMask) { - self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable - self::initHashMask(); + if (!$gid = self::$gid) { + $gid = self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable } - $gid = self::$gid; - $hashMask = self::$hashMask; - $hashOffset = self::$hashOffset; $arrayStub = new Stub(); $arrayStub->type = Stub::TYPE_ARRAY; $fromObjCast = false; @@ -74,22 +69,29 @@ protected function doClone($var) if (\is_int($k)) { continue; } - foreach (array($k => true) as $gk => $gv) { + foreach ([$k => true] as $gk => $gv) { } if ($gk !== $k) { $fromObjCast = true; - $refs = $vals = \array_values($queue[$i]); + $refs = $vals = array_values($queue[$i]); break; } } } foreach ($vals as $k => $v) { // $v is the original value or a stub object in case of hard references - $refs[$k] = $cookie; - if ($zvalIsRef = $vals[$k] === $cookie) { + + if (\PHP_VERSION_ID >= 70400) { + $zvalIsRef = null !== \ReflectionReference::fromArrayElement($vals, $k); + } else { + $refs[$k] = $cookie; + $zvalIsRef = $vals[$k] === $cookie; + } + + if ($zvalIsRef) { $vals[$k] = &$stub; // Break hard references to make $queue completely unset($stub); // independent from the original structure - if ($v instanceof Stub && isset($hardRefs[\spl_object_hash($v)])) { + if ($v instanceof Stub && isset($hardRefs[spl_object_id($v)])) { $vals[$k] = $refs[$k] = $v; if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { ++$v->value->refCount; @@ -99,7 +101,7 @@ protected function doClone($var) } $refs[$k] = $vals[$k] = new Stub(); $refs[$k]->value = $v; - $h = \spl_object_hash($refs[$k]); + $h = spl_object_id($refs[$k]); $hardRefs[$h] = &$refs[$k]; $values[$h] = $v; $vals[$k]->handle = ++$refsCounter; @@ -117,22 +119,22 @@ protected function doClone($var) if ('' === $v) { continue 2; } - if (!\preg_match('//u', $v)) { + if (!preg_match('//u', $v)) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { $stub->cut = $cut; - $stub->value = \substr($v, 0, -$cut); + $stub->value = substr($v, 0, -$cut); } else { $stub->value = $v; } - } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = \mb_strlen($v, 'UTF-8') - $maxString) { + } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_UTF8; $stub->cut = $cut; - $stub->value = \mb_substr($v, 0, $maxString, 'UTF-8'); + $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); } else { continue 2; } @@ -163,7 +165,7 @@ protected function doClone($var) // Happens with copies of $GLOBALS if (isset($v[$gid])) { unset($v[$gid]); - $a = array(); + $a = []; foreach ($v as $gk => &$gv) { $a[$gk] = &$gv; } @@ -178,7 +180,7 @@ protected function doClone($var) case \is_object($v): case $v instanceof \__PHP_Incomplete_Class: - if (empty($objRefs[$h = $hashMask ^ \hexdec(\substr(\spl_object_hash($v), $hashOffset, \PHP_INT_SIZE))])) { + if (empty($objRefs[$h = spl_object_id($v)])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = \get_class($v); @@ -189,8 +191,7 @@ protected function doClone($var) if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { break; } - $h = $hashMask ^ \hexdec(\substr(\spl_object_hash($stub->value), $hashOffset, \PHP_INT_SIZE)); - $stub->handle = $h; + $stub->handle = $h = spl_object_id($stub->value); } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { @@ -200,6 +201,7 @@ protected function doClone($var) } if (empty($objRefs[$h])) { $objRefs[$h] = $stub; + $objects[] = $v; } else { $stub = $objRefs[$h]; ++$stub->refCount; @@ -211,7 +213,7 @@ protected function doClone($var) if (empty($resRefs[$h = (int) $v])) { $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; - if ('Unknown' === $stub->class = @\get_resource_type($v)) { + if ('Unknown' === $stub->class = @get_resource_type($v)) { $stub->class = 'Closed'; } $stub->value = $v; @@ -254,12 +256,12 @@ protected function doClone($var) if ($arrayStub === $stub) { if ($arrayStub->cut) { - $stub = array($arrayStub->cut, $arrayStub->class => $arrayStub->position); + $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; $arrayStub->cut = 0; } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; } else { - self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = array($arrayStub->class => $arrayStub->position); + self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; } } @@ -273,10 +275,10 @@ protected function doClone($var) if ($fromObjCast) { $fromObjCast = false; $refs = $vals; - $vals = array(); + $vals = []; $j = -1; foreach ($queue[$i] as $k => $v) { - foreach (array($k => true) as $gk => $gv) { + foreach ([$k => true] as $gk => $gv) { } if ($gk !== $k) { $vals = (object) $vals; @@ -297,31 +299,4 @@ protected function doClone($var) return $queue; } - - private static function initHashMask() - { - $obj = (object) array(); - self::$hashOffset = 16 - PHP_INT_SIZE; - self::$hashMask = -1; - - if (\defined('HHVM_VERSION')) { - self::$hashOffset += 16; - } else { - // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below - $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush'); - foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { - if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { - $frame['line'] = 0; - break; - } - } - if (!empty($frame['line'])) { - ob_start(); - debug_zval_dump($obj); - self::$hashMask = (int) substr(ob_get_clean(), 17); - } - } - - self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, PHP_INT_SIZE)); - } } diff --git a/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php b/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php new file mode 100644 index 0000000..dc77d03 --- /dev/null +++ b/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Command\Descriptor; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * Describe collected data clones for cli output. + * + * @author Maxime Steinhausser + * + * @final + */ +class CliDescriptor implements DumpDescriptorInterface +{ + private $dumper; + private $lastIdentifier; + private $supportsHref; + + public function __construct(CliDumper $dumper) + { + $this->dumper = $dumper; + $this->supportsHref = method_exists(OutputFormatterStyle::class, 'setHref'); + } + + public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void + { + $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output); + $this->dumper->setColors($output->isDecorated()); + + $rows = [['date', date('r', $context['timestamp'])]]; + $lastIdentifier = $this->lastIdentifier; + $this->lastIdentifier = $clientId; + + $section = "Received from client #$clientId"; + if (isset($context['request'])) { + $request = $context['request']; + $this->lastIdentifier = $request['identifier']; + $section = sprintf('%s %s', $request['method'], $request['uri']); + if ($controller = $request['controller']) { + $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")]; + } + } elseif (isset($context['cli'])) { + $this->lastIdentifier = $context['cli']['identifier']; + $section = '$ '.$context['cli']['command_line']; + } + + if ($this->lastIdentifier !== $lastIdentifier) { + $io->section($section); + } + + if (isset($context['source'])) { + $source = $context['source']; + $sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']); + $fileLink = $source['file_link'] ?? null; + if ($this->supportsHref && $fileLink) { + $sourceInfo = sprintf('%s', $fileLink, $sourceInfo); + } + $rows[] = ['source', $sourceInfo]; + $file = $source['file_relative'] ?? $source['file']; + $rows[] = ['file', $file]; + } + + $io->table([], $rows); + + if (!$this->supportsHref && isset($fileLink)) { + $io->writeln(['Open source in your IDE/browser:', $fileLink]); + $io->newLine(); + } + + $this->dumper->dump($data); + $io->newLine(); + } +} diff --git a/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php b/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php new file mode 100644 index 0000000..267d27b --- /dev/null +++ b/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Command\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * @author Maxime Steinhausser + */ +interface DumpDescriptorInterface +{ + public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; +} diff --git a/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php b/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php new file mode 100644 index 0000000..35a203b --- /dev/null +++ b/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Command\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; + +/** + * Describe collected data clones for html output. + * + * @author Maxime Steinhausser + * + * @final + */ +class HtmlDescriptor implements DumpDescriptorInterface +{ + private $dumper; + private $initialized = false; + + public function __construct(HtmlDumper $dumper) + { + $this->dumper = $dumper; + } + + public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void + { + if (!$this->initialized) { + $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); + $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); + $output->writeln(""); + $this->initialized = true; + } + + $title = '-'; + if (isset($context['request'])) { + $request = $context['request']; + $controller = "{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}"; + $title = sprintf('%s %s', $request['method'], $uri = $request['uri'], $uri); + $dedupIdentifier = $request['identifier']; + } elseif (isset($context['cli'])) { + $title = '$ '.$context['cli']['command_line']; + $dedupIdentifier = $context['cli']['identifier']; + } else { + $dedupIdentifier = uniqid('', true); + } + + $sourceDescription = ''; + if (isset($context['source'])) { + $source = $context['source']; + $projectDir = $source['project_dir'] ?? null; + $sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']); + if (isset($source['file_link'])) { + $sourceDescription = sprintf('%s', $source['file_link'], $sourceDescription); + } + } + + $isoDate = $this->extractDate($context, 'c'); + $tags = array_filter([ + 'controller' => $controller ?? null, + 'project dir' => $projectDir ?? null, + ]); + + $output->writeln(<< +
+
+

$title

+ +
+ {$this->renderTags($tags)} +
+
+

+ $sourceDescription +

+ {$this->dumper->dump($data, true)} +
+ +HTML + ); + } + + private function extractDate(array $context, string $format = 'r'): string + { + return date($format, $context['timestamp']); + } + + private function renderTags(array $tags): string + { + if (!$tags) { + return ''; + } + + $renderedTags = ''; + foreach ($tags as $key => $value) { + $renderedTags .= sprintf('
  • %s%s
  • ', $key, $value); + } + + return << +
      + $renderedTags +
    + +HTML; + } +} diff --git a/vendor/symfony/var-dumper/Command/ServerDumpCommand.php b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php new file mode 100644 index 0000000..c8a61da --- /dev/null +++ b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; +use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; +use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\Server\DumpServer; + +/** + * Starts a dump server to collect and output dumps on a single place with multiple formats support. + * + * @author Maxime Steinhausser + * + * @final + */ +class ServerDumpCommand extends Command +{ + protected static $defaultName = 'server:dump'; + + private $server; + + /** @var DumpDescriptorInterface[] */ + private $descriptors; + + public function __construct(DumpServer $server, array $descriptors = []) + { + $this->server = $server; + $this->descriptors = $descriptors + [ + 'cli' => new CliDescriptor(new CliDumper()), + 'html' => new HtmlDescriptor(new HtmlDumper()), + ]; + + parent::__construct(); + } + + protected function configure() + { + $availableFormats = implode(', ', array_keys($this->descriptors)); + + $this + ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', $availableFormats), 'cli') + ->setDescription('Starts a dump server that collects and displays dumps in a single place') + ->setHelp(<<<'EOF' +%command.name% starts a dump server that collects and displays +dumps in a single place for debugging you application: + + php %command.full_name% + +You can consult dumped data in HTML format in your browser by providing the --format=html option +and redirecting the output to a file: + + php %command.full_name% --format="html" > dump.html + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $format = $input->getOption('format'); + + if (!$descriptor = $this->descriptors[$format] ?? null) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format)); + } + + $errorIo = $io->getErrorStyle(); + $errorIo->title('Symfony Var Dumper Server'); + + $this->server->start(); + + $errorIo->success(sprintf('Server listening on %s', $this->server->getHost())); + $errorIo->comment('Quit the server with CONTROL-C.'); + + $this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) { + $descriptor->describe($io, $data, $context, $clientId); + }); + } +} diff --git a/vendor/symfony/var-dumper/Dumper/AbstractDumper.php b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php index 228f8f2..4079fd3 100644 --- a/vendor/symfony/var-dumper/Dumper/AbstractDumper.php +++ b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php @@ -35,16 +35,16 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface protected $indentPad = ' '; protected $flags; - private $charset; + private $charset = ''; /** * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput - * @param string $charset The default character encoding to use for non-UTF8 strings + * @param string|null $charset The default character encoding to use for non-UTF8 strings * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation */ - public function __construct($output = null, $charset = null, $flags = 0) + public function __construct($output = null, string $charset = null, int $flags = 0) { - $this->flags = (int) $flags; + $this->flags = $flags; $this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'); $this->decimalPoint = localeconv(); $this->decimalPoint = $this->decimalPoint['decimal_point']; @@ -73,7 +73,7 @@ public function setOutput($output) $output = fopen($output, 'wb'); } $this->outputStream = $output; - $this->lineDumper = array($this, 'echoLine'); + $this->lineDumper = [$this, 'echoLine']; } return $prev; @@ -116,7 +116,6 @@ public function setIndentPad($pad) /** * Dumps a Data object. * - * @param Data $data A Data object * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump * * @return string|null The dump as string when $output is true @@ -154,6 +153,8 @@ public function dump(Data $data, $output = null) setlocale(LC_NUMERIC, $locale); } } + + return null; } /** @@ -164,7 +165,7 @@ public function dump(Data $data, $output = null) */ protected function dumpLine($depth) { - \call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad); + ($this->lineDumper)($this->line, $depth, $this->indentPad); $this->line = ''; } @@ -185,13 +186,13 @@ protected function echoLine($line, $depth, $indentPad) /** * Converts a non-UTF-8 string to UTF-8. * - * @param string $s The non-UTF-8 string to convert + * @param string|null $s The non-UTF-8 string to convert * - * @return string The string converted to UTF-8 + * @return string|null The string converted to UTF-8 */ protected function utf8Encode($s) { - if (preg_match('//u', $s)) { + if (null === $s || preg_match('//u', $s)) { return $s; } diff --git a/vendor/symfony/var-dumper/Dumper/CliDumper.php b/vendor/symfony/var-dumper/Dumper/CliDumper.php index 419cfeb..b146fc8 100644 --- a/vendor/symfony/var-dumper/Dumper/CliDumper.php +++ b/vendor/symfony/var-dumper/Dumper/CliDumper.php @@ -26,9 +26,9 @@ class CliDumper extends AbstractDumper protected $colors; protected $maxStringWidth = 0; - protected $styles = array( + protected $styles = [ // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics - 'default' => '38;5;208', + 'default' => '0;38;5;208', 'num' => '1;38;5;38', 'const' => '1;38;5;208', 'str' => '1;38;5;113', @@ -40,31 +40,37 @@ class CliDumper extends AbstractDumper 'meta' => '38;5;170', 'key' => '38;5;113', 'index' => '38;5;38', - ); + ]; protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/'; - protected static $controlCharsMap = array( + protected static $controlCharsMap = [ "\t" => '\t', "\n" => '\n', "\v" => '\v', "\f" => '\f', "\r" => '\r', "\033" => '\e', - ); + ]; protected $collapseNextHash = false; protected $expandNextHash = false; + private $displayOptions = [ + 'fileLinkFormat' => null, + ]; + + private $handlesHrefGracefully; + /** * {@inheritdoc} */ - public function __construct($output = null, $charset = null, $flags = 0) + public function __construct($output = null, string $charset = null, int $flags = 0) { parent::__construct($output, $charset, $flags); if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI - $this->setStyles(array( + $this->setStyles([ 'default' => '31', 'num' => '1;34', 'const' => '1;31', @@ -74,8 +80,10 @@ public function __construct($output = null, $charset = null, $flags = 0) 'meta' => '35', 'key' => '32', 'index' => '34', - )); + ]); } + + $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; } /** @@ -108,6 +116,16 @@ public function setStyles(array $styles) $this->styles = $styles + $this->styles; } + /** + * Configures display options. + * + * @param array $displayOptions A map of display options to customize the behavior + */ + public function setDisplayOptions(array $displayOptions) + { + $this->displayOptions = $displayOptions + $this->displayOptions; + } + /** * {@inheritdoc} */ @@ -152,7 +170,7 @@ public function dumpScalar(Cursor $cursor, $type, $value) break; default: - $attr += array('value' => $this->utf8Encode($value)); + $attr += ['value' => $this->utf8Encode($value)]; $value = $this->utf8Encode($type); break; } @@ -177,10 +195,10 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut) $this->line .= '""'; $this->endValue($cursor); } else { - $attr += array( + $attr += [ 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, 'binary' => $bin, - ); + ]; $str = explode("\n", $str); if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { unset($str[1]); @@ -256,6 +274,7 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut) public function enterHash(Cursor $cursor, $type, $class, $hasChild) { $this->dumpKey($cursor); + $attr = $cursor->attr; if ($this->collapseNextHash) { $cursor->skipChildren = true; @@ -264,17 +283,17 @@ public function enterHash(Cursor $cursor, $type, $class, $hasChild) $class = $this->utf8Encode($class); if (Cursor::HASH_OBJECT === $type) { - $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{'; + $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; } elseif (Cursor::HASH_RESOURCE === $type) { - $prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' '); + $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); } else { - $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; + $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class, $attr).' [' : '['; } - if ($cursor->softRefCount || 0 < $cursor->softRefHandle) { - $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), array('count' => $cursor->softRefCount)); + if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { + $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { - $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, array('count' => $cursor->hardRefCount)); + $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { $prefix = substr($prefix, 0, -1); } @@ -291,17 +310,19 @@ public function enterHash(Cursor $cursor, $type, $class, $hasChild) */ public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut) { - $this->dumpEllipsis($cursor, $hasChild, $cut); - $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); + if (empty($cursor->attr['cut_hash'])) { + $this->dumpEllipsis($cursor, $hasChild, $cut); + $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); + } + $this->endValue($cursor); } /** * Dumps an ellipsis for cut children. * - * @param Cursor $cursor The Cursor position in the dump - * @param bool $hasChild When the dump of the hash has child item - * @param int $cut The number of items the hash has been cut by + * @param bool $hasChild When the dump of the hash has child item + * @param int $cut The number of items the hash has been cut by */ protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut) { @@ -318,8 +339,6 @@ protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut) /** * Dumps a key in a hash structure. - * - * @param Cursor $cursor The Cursor position in the dump */ protected function dumpKey(Cursor $cursor) { @@ -327,7 +346,7 @@ protected function dumpKey(Cursor $cursor) if ($cursor->hashKeyIsBinary) { $key = $this->utf8Encode($key); } - $attr = array('binary' => $cursor->hashKeyIsBinary); + $attr = ['binary' => $cursor->hashKeyIsBinary]; $bin = $cursor->hashKeyIsBinary ? 'b' : ''; $style = 'key'; switch ($cursor->hashType) { @@ -364,7 +383,7 @@ protected function dumpKey(Cursor $cursor) $style = 'meta'; if (isset($key[0][1])) { parse_str(substr($key[0], 1), $attr); - $attr += array('binary' => $cursor->hashKeyIsBinary); + $attr += ['binary' => $cursor->hashKeyIsBinary]; } break; case '*': @@ -389,13 +408,13 @@ protected function dumpKey(Cursor $cursor) $this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': '); } else { // This case should not happen - $this->line .= '-'.$bin.'"'.$this->style('private', $key, array('class' => '')).'": '; + $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; } break; } if ($cursor->hardRefTo) { - $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), array('count' => $cursor->hardRefCount)).' '; + $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; } } } @@ -409,12 +428,16 @@ protected function dumpKey(Cursor $cursor) * * @return string The value with style decoration */ - protected function style($style, $value, $attr = array()) + protected function style($style, $value, $attr = []) { if (null === $this->colors) { $this->colors = $this->supportsColors(); } + if (null === $this->handlesHrefGracefully) { + $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION'); + } + if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { $prefix = substr($value, 0, -$attr['ellipsis']); if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) { @@ -427,14 +450,14 @@ protected function style($style, $value, $attr = array()) $value = substr($value, -$attr['ellipsis']); } - return $this->style('default', $prefix).$this->style($style, $value); - } + $value = $this->style('default', $prefix).$this->style($style, $value); - $style = $this->styles[$style]; + goto href; + } $map = static::$controlCharsMap; $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; - $endCchr = $this->colors ? "\033[m\033[{$style}m" : ''; + $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { $s = $startCchr; $c = $c[$i = 0]; @@ -449,7 +472,7 @@ protected function style($style, $value, $attr = array()) if ($cchrCount && "\033" === $value[0]) { $value = substr($value, \strlen($startCchr)); } else { - $value = "\033[{$style}m".$value; + $value = "\033[{$this->styles[$style]}m".$value; } if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) { $value = substr($value, 0, -\strlen($endCchr)); @@ -458,6 +481,22 @@ protected function style($style, $value, $attr = array()) } } + href: + if ($this->colors && $this->handlesHrefGracefully) { + if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) { + if ('note' === $style) { + $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; + } else { + $attr['href'] = $href; + } + } + if (isset($attr['href'])) { + $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; + } + } elseif ($attr['if_links'] ?? false) { + return ''; + } + return $value; } @@ -495,7 +534,7 @@ protected function supportsColors() } } - $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); + $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; return static::$defaultColors = $this->hasColorSupport($h); @@ -514,6 +553,10 @@ protected function dumpLine($depth, $endOfValue = false) protected function endValue(Cursor $cursor) { + if (-1 === $cursor->hashType) { + return; + } + if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { $this->line .= ','; @@ -532,15 +575,18 @@ protected function endValue(Cursor $cursor) * https://github.com/composer/xdebug-handler * * @param mixed $stream A CLI output stream - * - * @return bool */ - private function hasColorSupport($stream) + private function hasColorSupport($stream): bool { if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { return false; } + // Follow https://no-color.org/ + if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { + return false; + } + if ('Hyper' === getenv('TERM_PROGRAM')) { return true; } @@ -572,10 +618,8 @@ private function hasColorSupport($stream) * Note that this does not check an output stream, but relies on environment * variables from known implementations, or a PHP and Windows version that * supports true color. - * - * @return bool */ - private function isWindowsTrueColor() + private function isWindowsTrueColor(): bool { $result = 183 <= getenv('ANSICON_VER') || 'ON' === getenv('ConEmuANSI') @@ -594,4 +638,13 @@ private function isWindowsTrueColor() return $result; } + + private function getSourceLink(string $file, int $line) + { + if ($fmt = $this->displayOptions['fileLinkFormat']) { + return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); + } + + return false; + } } diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php new file mode 100644 index 0000000..38f8789 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper\ContextProvider; + +/** + * Tries to provide context on CLI. + * + * @author Maxime Steinhausser + */ +final class CliContextProvider implements ContextProviderInterface +{ + public function getContext(): ?array + { + if ('cli' !== \PHP_SAPI) { + return null; + } + + return [ + 'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []), + 'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']), + ]; + } +} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php new file mode 100644 index 0000000..38ef3b0 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper\ContextProvider; + +/** + * Interface to provide contextual data about dump data clones sent to a server. + * + * @author Maxime Steinhausser + */ +interface ContextProviderInterface +{ + /** + * @return array|null Context data or null if unable to provide any context + */ + public function getContext(): ?array; +} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php new file mode 100644 index 0000000..3684a47 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper\ContextProvider; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\VarDumper\Caster\ReflectionCaster; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +/** + * Tries to provide context from a request. + * + * @author Maxime Steinhausser + */ +final class RequestContextProvider implements ContextProviderInterface +{ + private $requestStack; + private $cloner; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + $this->cloner = new VarCloner(); + $this->cloner->setMaxItems(0); + $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); + } + + public function getContext(): ?array + { + if (null === $request = $this->requestStack->getCurrentRequest()) { + return null; + } + + $controller = $request->attributes->get('_controller'); + + return [ + 'uri' => $request->getUri(), + 'method' => $request->getMethod(), + 'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller, + 'identifier' => spl_object_hash($request), + ]; + } +} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php new file mode 100644 index 0000000..6f4caba --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper\ContextProvider; + +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\VarDumper; +use Twig\Template; + +/** + * Tries to provide context from sources (class name, file, line, code excerpt, ...). + * + * @author Nicolas Grekas + * @author Maxime Steinhausser + */ +final class SourceContextProvider implements ContextProviderInterface +{ + private $limit; + private $charset; + private $projectDir; + private $fileLinkFormatter; + + public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9) + { + $this->charset = $charset; + $this->projectDir = $projectDir; + $this->fileLinkFormatter = $fileLinkFormatter; + $this->limit = $limit; + } + + public function getContext(): ?array + { + $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit); + + $file = $trace[1]['file']; + $line = $trace[1]['line']; + $name = false; + $fileExcerpt = false; + + for ($i = 2; $i < $this->limit; ++$i) { + if (isset($trace[$i]['class'], $trace[$i]['function']) + && 'dump' === $trace[$i]['function'] + && VarDumper::class === $trace[$i]['class'] + ) { + $file = $trace[$i]['file'] ?? $file; + $line = $trace[$i]['line'] ?? $line; + + while (++$i < $this->limit) { + if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) { + $file = $trace[$i]['file']; + $line = $trace[$i]['line']; + + break; + } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) { + $template = $trace[$i]['object']; + $name = $template->getTemplateName(); + $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); + $info = $template->getDebugInfo(); + if (isset($info[$trace[$i - 1]['line']])) { + $line = $info[$trace[$i - 1]['line']]; + $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; + + if ($src) { + $src = explode("\n", $src); + $fileExcerpt = []; + + for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { + $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; + } + + $fileExcerpt = '
      '.implode("\n", $fileExcerpt).'
    '; + } + } + break; + } + } + break; + } + } + + if (false === $name) { + $name = str_replace('\\', '/', $file); + $name = substr($name, strrpos($name, '/') + 1); + } + + $context = ['name' => $name, 'file' => $file, 'line' => $line]; + $context['file_excerpt'] = $fileExcerpt; + + if (null !== $this->projectDir) { + $context['project_dir'] = $this->projectDir; + if (0 === strpos($file, $this->projectDir)) { + $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); + } + } + + if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) { + $context['file_link'] = $fileLink; + } + + return $context; + } + + private function htmlEncode(string $s): string + { + $html = ''; + + $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + + $cloner = new VarCloner(); + $dumper->dump($cloner->cloneVar($s)); + + return substr(strip_tags($html), 1, -1); + } +} diff --git a/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php b/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php new file mode 100644 index 0000000..7638417 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper; + +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; + +/** + * @author Kévin Thérage + */ +class ContextualizedDumper implements DataDumperInterface +{ + private $wrappedDumper; + private $contextProviders; + + /** + * @param ContextProviderInterface[] $contextProviders + */ + public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) + { + $this->wrappedDumper = $wrappedDumper; + $this->contextProviders = $contextProviders; + } + + public function dump(Data $data) + { + $context = []; + foreach ($this->contextProviders as $contextProvider) { + $context[\get_class($contextProvider)] = $contextProvider->getContext(); + } + + $this->wrappedDumper->dump($data->withContext($context)); + } +} diff --git a/vendor/symfony/var-dumper/Dumper/HtmlDumper.php b/vendor/symfony/var-dumper/Dumper/HtmlDumper.php index d498e12..e7a6e32 100644 --- a/vendor/symfony/var-dumper/Dumper/HtmlDumper.php +++ b/vendor/symfony/var-dumper/Dumper/HtmlDumper.php @@ -23,6 +23,41 @@ class HtmlDumper extends CliDumper { public static $defaultOutput = 'php://output'; + protected static $themes = [ + 'dark' => [ + 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', + 'num' => 'font-weight:bold; color:#1299DA', + 'const' => 'font-weight:bold', + 'str' => 'font-weight:bold; color:#56DB3A', + 'note' => 'color:#1299DA', + 'ref' => 'color:#A0A0A0', + 'public' => 'color:#FFFFFF', + 'protected' => 'color:#FFFFFF', + 'private' => 'color:#FFFFFF', + 'meta' => 'color:#B729D9', + 'key' => 'color:#56DB3A', + 'index' => 'color:#1299DA', + 'ellipsis' => 'color:#FF8400', + 'ns' => 'user-select:none;', + ], + 'light' => [ + 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', + 'num' => 'font-weight:bold; color:#1299DA', + 'const' => 'font-weight:bold', + 'str' => 'font-weight:bold; color:#629755;', + 'note' => 'color:#6897BB', + 'ref' => 'color:#6E6E6E', + 'public' => 'color:#262626', + 'protected' => 'color:#262626', + 'private' => 'color:#262626', + 'meta' => 'color:#B729D9', + 'key' => 'color:#789339', + 'index' => 'color:#1299DA', + 'ellipsis' => 'color:#CC7832', + 'ns' => 'user-select:none;', + ], + ]; + protected $dumpHeader; protected $dumpPrefix = '
    ';
         protected $dumpSuffix = '
    '; @@ -30,37 +65,24 @@ class HtmlDumper extends CliDumper protected $colors = true; protected $headerIsDumped = false; protected $lastDepth = -1; - protected $styles = array( - 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', - 'num' => 'font-weight:bold; color:#1299DA', - 'const' => 'font-weight:bold', - 'str' => 'font-weight:bold; color:#56DB3A', - 'note' => 'color:#1299DA', - 'ref' => 'color:#A0A0A0', - 'public' => 'color:#FFFFFF', - 'protected' => 'color:#FFFFFF', - 'private' => 'color:#FFFFFF', - 'meta' => 'color:#B729D9', - 'key' => 'color:#56DB3A', - 'index' => 'color:#1299DA', - 'ellipsis' => 'color:#FF8400', - ); - - private $displayOptions = array( + protected $styles; + + private $displayOptions = [ 'maxDepth' => 1, 'maxStringLength' => 160, 'fileLinkFormat' => null, - ); - private $extraDisplayOptions = array(); + ]; + private $extraDisplayOptions = []; /** * {@inheritdoc} */ - public function __construct($output = null, $charset = null, $flags = 0) + public function __construct($output = null, string $charset = null, int $flags = 0) { AbstractDumper::__construct($output, $charset, $flags); $this->dumpId = 'sf-dump-'.mt_rand(); $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->styles = static::$themes['dark'] ?? self::$themes['dark']; } /** @@ -72,6 +94,15 @@ public function setStyles(array $styles) $this->styles = $styles + $this->styles; } + public function setTheme(string $themeName) + { + if (!isset(static::$themes[$themeName])) { + throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class)); + } + + $this->setStyles(static::$themes[$themeName]); + } + /** * Configures display options. * @@ -108,7 +139,7 @@ public function setDumpBoundaries($prefix, $suffix) /** * {@inheritdoc} */ - public function dump(Data $data, $output = null, array $extraDisplayOptions = array()) + public function dump(Data $data, $output = null, array $extraDisplayOptions = []) { $this->extraDisplayOptions = $extraDisplayOptions; $result = parent::dump($data, $output); @@ -283,13 +314,21 @@ function resetHighlightedNodes(root) { } function a(e, f) { - addEventListener(root, e, function (e) { + addEventListener(root, e, function (e, n) { if ('A' == e.target.tagName) { f(e.target, e); } else if ('A' == e.target.parentNode.tagName) { f(e.target.parentNode, e); - } else if (e.target.nextElementSibling && 'A' == e.target.nextElementSibling.tagName) { - f(e.target.nextElementSibling, e, true); + } else { + n = /\bsf-dump-ellipsis\b/.test(e.target.className) ? e.target.parentNode : e.target; + + if ((n = n.nextElementSibling) && 'A' == n.tagName) { + if (!/\bsf-dump-toggle\b/.test(n.className)) { + n = n.nextElementSibling || n; + } + + f(n, e, true); + } } }); }; @@ -438,7 +477,7 @@ function xpathHasClass(className) { return this.current(); } this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0; - + return this.current(); }, previous: function () { @@ -446,7 +485,7 @@ function xpathHasClass(className) { return this.current(); } this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1); - + return this.current(); }, isEmpty: function () { @@ -469,10 +508,18 @@ function xpathHasClass(className) { function showCurrent(state) { - var currentNode = state.current(); + var currentNode = state.current(), currentRect, searchRect; if (currentNode) { reveal(currentNode); highlight(root, currentNode, state.nodes); + if ('scrollIntoView' in currentNode) { + currentNode.scrollIntoView(true); + currentRect = currentNode.getBoundingClientRect(); + searchRect = search.getBoundingClientRect(); + if (currentRect.top < (searchRect.top + searchRect.height)) { + window.scrollBy(0, -(searchRect.top + searchRect.height + 5)); + } + } } counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count(); } @@ -483,14 +530,10 @@ function showCurrent(state) 0 of 0<\/span>